Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  properties.mako.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

// This file is a Mako template: http://www.makotemplates.org/

// Please note that valid Rust syntax may be mangled by the Mako parser.
// For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code
// can be escaped. In the above example, Vec<<&Foo> or Vec< &Foo> achieves the desired result of Vec<&Foo>.

<%namespace name="helpers" file="/helpers.mako.rs" />

use app_units::Au;
use servo_arc::{Arc, UniqueArc};
use std::{ops, ptr};
use std::{fmt, mem};

#[cfg(feature = "servo")] use euclid::SideOffsets2D;
#[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, nsCSSPropertyID};
#[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin;
#[cfg(feature = "servo")] use crate::computed_values;
use crate::logical_geometry::WritingMode;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::computed_value_flags::*;
use cssparser::Parser;
use crate::media_queries::Device;
use crate::parser::ParserContext;
use crate::selector_parser::PseudoElement;
use crate::stylist::Stylist;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin};
use crate::logical_geometry::{LogicalAxis, LogicalCorner, LogicalSide};
use crate::use_counters::UseCounters;
use crate::rule_tree::StrongRuleNode;
use crate::str::CssStringWriter;
use crate::values::{
    computed,
    resolved,
    specified::{font::SystemFont, length::LineHeightBase, color::ColorSchemeFlags},
};
use std::cell::Cell;
use super::{
    PropertyDeclarationId, PropertyId, NonCustomPropertyId,
    NonCustomPropertyIdSet, PropertyFlags, SourcePropertyDeclaration,
    LonghandIdSet, VariableDeclaration, CustomDeclaration,
    WideKeywordDeclaration, NonCustomPropertyIterator,
};

<%!
    from collections import defaultdict
    from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \
                     to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS, PRIORITARY_PROPERTIES
    import os.path
%>

/// Conversion with fewer impls than From/Into
pub trait MaybeBoxed<Out> {
    /// Convert
    fn maybe_boxed(self) -> Out;
}

impl<T> MaybeBoxed<T> for T {
    #[inline]
    fn maybe_boxed(self) -> T { self }
}

impl<T> MaybeBoxed<Box<T>> for T {
    #[inline]
    fn maybe_boxed(self) -> Box<T> { Box::new(self) }
}

macro_rules! expanded {
    ( $( $name: ident: $value: expr ),+ ) => {
        expanded!( $( $name: $value, )+ )
    };
    ( $( $name: ident: $value: expr, )+ ) => {
        Longhands {
            $(
                $name: MaybeBoxed::maybe_boxed($value),
            )+
        }
    }
}

/// A module with all the code for longhand properties.
#[allow(missing_docs)]
pub mod longhands {
    % for style_struct in data.style_structs:
    <% data.current_style_struct = style_struct %>
    <%include file="/longhands/${style_struct.name_lower}.mako.rs" />
    % endfor
}


% if engine == "gecko":
#[allow(unsafe_code, missing_docs)]
pub mod gecko {
    <%include file="/gecko.mako.rs" />
}
% endif


macro_rules! unwrap_or_initial {
    ($prop: ident) => (unwrap_or_initial!($prop, $prop));
    ($prop: ident, $expr: expr) =>
        ($expr.unwrap_or_else(|| $prop::get_initial_specified_value()));
}

/// A module with code for all the shorthand css properties, and a few
/// serialization helpers.
#[allow(missing_docs)]
pub mod shorthands {
    use cssparser::Parser;
    use crate::parser::{Parse, ParserContext};
    use style_traits::{ParseError, StyleParseErrorKind};
    use crate::values::specified;

    % for style_struct in data.style_structs:
    <%include file="/shorthands/${style_struct.name_lower}.mako.rs" />
    % endfor

    // We didn't define the 'all' shorthand using the regular helpers:shorthand
    // mechanism, since it causes some very large types to be generated.
    //
    // Also, make sure logical properties appear before its physical
    // counter-parts, in order to prevent bugs like:
    //
    //   https://bugzilla.mozilla.org/show_bug.cgi?id=1410028
    //
    // FIXME(emilio): Adopt the resolution from:
    //
    //   https://github.com/w3c/csswg-drafts/issues/1898
    //
    // when there is one, whatever that is.
    <%
        logical_longhands = []
        other_longhands = []

        for p in data.longhands:
            if p.name in ['direction', 'unicode-bidi']:
                continue;
            if not p.enabled_in_content() and not p.experimental(engine):
                continue;
            if "Style" not in p.rule_types_allowed_names():
                continue;
            if p.logical:
                logical_longhands.append(p.name)
            else:
                other_longhands.append(p.name)

        data.declare_shorthand(
            "all",
            logical_longhands + other_longhands,
            engines="gecko servo",
            spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
        )
        ALL_SHORTHAND_LEN = len(logical_longhands) + len(other_longhands);
    %>
}

<%
    from itertools import groupby

    # After this code, `data.longhands` is sorted in the following order:
    # - first all keyword variants and all variants known to be Copy,
    # - second all the other variants, such as all variants with the same field
    #   have consecutive discriminants.
    # The variable `variants` contain the same entries as `data.longhands` in
    # the same order, but must exist separately to the data source, because
    # we then need to add three additional variants `WideKeywordDeclaration`,
    # `VariableDeclaration` and `CustomDeclaration`.

    variants = []
    for property in data.longhands:
        variants.append({
            "name": property.camel_case,
            "type": property.specified_type(),
            "doc": "`" + property.name + "`",
            "copy": property.specified_is_copy(),
        })

    groups = {}
    keyfunc = lambda x: x["type"]
    sortkeys = {}
    for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc):
        group = list(group)
        groups[ty] = group
        for v in group:
            if len(group) == 1:
                sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "")
            else:
                sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"])
    variants.sort(key=lambda x: sortkeys[x["name"]])

    # It is extremely important to sort the `data.longhands` array here so
    # that it is in the same order as `variants`, for `LonghandId` and
    # `PropertyDeclarationId` to coincide.
    data.longhands.sort(key=lambda x: sortkeys[x.camel_case])
%>

// WARNING: It is *really* important for the variants of `LonghandId`
// and `PropertyDeclaration` to be defined in the exact same order,
// with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,
// which don't exist in `LonghandId`.

<%
    extra_variants = [
        {
            "name": "CSSWideKeyword",
            "type": "WideKeywordDeclaration",
            "doc": "A CSS-wide keyword.",
            "copy": False,
        },
        {
            "name": "WithVariables",
            "type": "VariableDeclaration",
            "doc": "An unparsed declaration.",
            "copy": False,
        },
        {
            "name": "Custom",
            "type": "CustomDeclaration",
            "doc": "A custom property declaration.",
            "copy": False,
        },
    ]
    for v in extra_variants:
        variants.append(v)
        groups[v["type"]] = [v]
%>

/// Servo's representation for a property declaration.
#[derive(ToShmem)]
#[repr(u16)]
pub enum PropertyDeclaration {
    % for variant in variants:
    /// ${variant["doc"]}
    ${variant["name"]}(${variant["type"]}),
    % endfor
}

// There's one of these for each parsed declaration so it better be small.
size_of_test!(PropertyDeclaration, 32);

#[repr(C)]
struct PropertyDeclarationVariantRepr<T> {
    tag: u16,
    value: T
}

impl Clone for PropertyDeclaration {
    #[inline]
    fn clone(&self) -> Self {
        use self::PropertyDeclaration::*;

        <%
            [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])]
        %>

        let self_tag = unsafe {
            (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag
        };
        if self_tag <= LonghandId::${copy[-1]["name"]} as u16 {
            #[derive(Clone, Copy)]
            #[repr(u16)]
            enum CopyVariants {
                % for v in copy:
                _${v["name"]}(${v["type"]}),
                % endfor
            }

            unsafe {
                let mut out = mem::MaybeUninit::uninit();
                ptr::write(
                    out.as_mut_ptr() as *mut CopyVariants,
                    *(self as *const _ as *const CopyVariants),
                );
                return out.assume_init();
            }
        }

        // This function ensures that all properties not handled above
        // do not have a specified value implements Copy. If you hit
        // compile error here, you may want to add the type name into
        // Longhand.specified_is_copy in data.py.
        fn _static_assert_others_are_not_copy() {
            struct Helper<T>(T);
            trait AssertCopy { fn assert() {} }
            trait AssertNotCopy { fn assert() {} }
            impl<T: Copy> AssertCopy for Helper<T> {}
            % for ty in sorted(set(x["type"] for x in others)):
            impl AssertNotCopy for Helper<${ty}> {}
            Helper::<${ty}>::assert();
            % endfor
        }

        match *self {
            ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => {
                unsafe { debug_unreachable!() }
            }
            % for ty, vs in groupby(others, key=lambda x: x["type"]):
            <%
                vs = list(vs)
            %>
            % if len(vs) == 1:
            ${vs[0]["name"]}(ref value) => {
                ${vs[0]["name"]}(value.clone())
            }
            % else:
            ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => {
                unsafe {
                    let mut out = mem::MaybeUninit::uninit();
                    ptr::write(
                        out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>,
                        PropertyDeclarationVariantRepr {
                            tag: *(self as *const _ as *const u16),
                            value: value.clone(),
                        },
                    );
                    out.assume_init()
                }
            }
            % endif
            % endfor
        }
    }
}

impl PartialEq for PropertyDeclaration {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        use self::PropertyDeclaration::*;

        unsafe {
            let this_repr =
                &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);
            let other_repr =
                &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);
            if this_repr.tag != other_repr.tag {
                return false;
            }
            match *self {
                % for ty, vs in groupby(variants, key=lambda x: x["type"]):
                ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => {
                    let other_repr =
                        &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);
                    *this == other_repr.value
                }
                % endfor
            }
        }
    }
}

impl MallocSizeOf for PropertyDeclaration {
    #[inline]
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        use self::PropertyDeclaration::*;

        match *self {
            % for ty, vs in groupby(variants, key=lambda x: x["type"]):
            ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
                value.size_of(ops)
            }
            % endfor
        }
    }
}


impl PropertyDeclaration {
    /// Returns the given value for this declaration as a particular type.
    /// It's the caller's responsibility to guarantee that the longhand id has the right specified
    /// value representation.
    pub(crate) unsafe fn unchecked_value_as<T>(&self) -> &T {
        &(*(self as *const _ as *const PropertyDeclarationVariantRepr<T>)).value
    }

    /// Dumps the property declaration before crashing.
    #[cold]
    #[cfg(debug_assertions)]
    pub(crate) fn debug_crash(&self, reason: &str) {
        panic!("{}: {:?}", reason, self);
    }
    #[cfg(not(debug_assertions))]
    #[inline(always)]
    pub(crate) fn debug_crash(&self, _reason: &str) {}

    /// Returns whether this is a variant of the Longhand(Value) type, rather
    /// than one of the special variants in extra_variants.
    fn is_longhand_value(&self) -> bool {
        match *self {
            % for v in extra_variants:
            PropertyDeclaration::${v["name"]}(..) => false,
            % endfor
            _ => true,
        }
    }

    /// Like the method on ToCss, but without the type parameter to avoid
    /// accidentally monomorphizing this large function multiple times for
    /// different writers.
    pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
        use self::PropertyDeclaration::*;

        let mut dest = CssWriter::new(dest);
        match *self {
            % for ty, vs in groupby(variants, key=lambda x: x["type"]):
            ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
                value.to_css(&mut dest)
            }
            % endfor
        }
    }

    /// Returns the color value of a given property, for high-contrast-mode tweaks.
    pub(super) fn color_value(&self) -> Option<<&crate::values::specified::Color> {
        ${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")}
        <%
            # sanity check
            assert data.longhands_by_name["background-color"].predefined_type == "Color"

            color_specified_type = data.longhands_by_name["background-color"].specified_type()
        %>
        let id = self.id().as_longhand()?;
        if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() {
            return None;
        }
        let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>;
        Some(unsafe { &(*repr).value })
    }
}

/// A module with all the code related to animated properties.
///
/// This needs to be "included" by mako at least after all longhand modules,
/// given they populate the global data.
pub mod animated_properties {
    <%include file="/helpers/animated_properties.mako.rs" />
}

/// A module to group various interesting property counts.
pub mod property_counts {
    /// The number of (non-alias) longhand properties.
    pub const LONGHANDS: usize = ${len(data.longhands)};
    /// The number of (non-alias) shorthand properties.
    pub const SHORTHANDS: usize = ${len(data.shorthands)};
    /// The number of aliases.
    pub const ALIASES: usize = ${len(data.all_aliases())};
    /// The number of counted unknown properties.
    pub const COUNTED_UNKNOWN: usize = ${len(data.counted_unknown_properties)};
    /// The number of (non-alias) longhands and shorthands.
    pub const LONGHANDS_AND_SHORTHANDS: usize = LONGHANDS + SHORTHANDS;
    /// The number of non-custom properties.
    pub const NON_CUSTOM: usize = LONGHANDS_AND_SHORTHANDS + ALIASES;
    /// The number of prioritary properties that we have.
    pub const PRIORITARY: usize = ${len(PRIORITARY_PROPERTIES)};
    /// The max number of longhands that a shorthand other than "all" expands to.
    pub const MAX_SHORTHAND_EXPANDED: usize =
        ${max(len(s.sub_properties) for s in data.shorthands_except_all())};
    /// The max amount of longhands that the `all` shorthand will ever contain.
    pub const ALL_SHORTHAND_EXPANDED: usize = ${ALL_SHORTHAND_LEN};
    /// The number of animatable properties.
    pub const ANIMATABLE: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
}

% if engine == "gecko":
#[allow(dead_code)]
unsafe fn static_assert_nscsspropertyid() {
    % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
    std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
    % endfor
}
% endif

impl NonCustomPropertyId {
    /// Get the property name.
    #[inline]
    pub fn name(self) -> &'static str {
        static MAP: [&'static str; property_counts::NON_CUSTOM] = [
            % for property in data.longhands + data.shorthands + data.all_aliases():
            "${property.name}",
            % endfor
        ];
        MAP[self.0 as usize]
    }

    /// Returns whether this property is animatable.
    #[inline]
    pub fn is_animatable(self) -> bool {
        ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
        ANIMATABLE.contains(self)
    }

    /// Whether this property is enabled for all content right now.
    #[inline]
    pub(super) fn enabled_for_all_content(self) -> bool {
        ${static_non_custom_property_id_set(
            "EXPERIMENTAL",
            lambda p: p.experimental(engine)
        )}

        ${static_non_custom_property_id_set(
            "ALWAYS_ENABLED",
            lambda p: (not p.experimental(engine)) and p.enabled_in_content()
        )}

        let passes_pref_check = || {
            % if engine == "gecko":
                unsafe { structs::nsCSSProps_gPropertyEnabled[self.0 as usize] }
            % else:
                static PREF_NAME: [Option< &str>; ${
                    len(data.longhands) + len(data.shorthands) + len(data.all_aliases())
                }] = [
                    % for property in data.longhands + data.shorthands + data.all_aliases():
                        <%
                            pref = getattr(property, "servo_pref")
                        %>
                        % if pref:
                            Some("${pref}"),
                        % else:
                            None,
                        % endif
                    % endfor
                ];
                let pref = match PREF_NAME[self.0 as usize] {
                    None => return true,
                    Some(pref) => pref,
                };

                style_config::get_bool(pref)
            % endif
        };

        if ALWAYS_ENABLED.contains(self) {
            return true
        }

        if EXPERIMENTAL.contains(self) && passes_pref_check() {
            return true
        }

        false
    }

    /// Returns whether a given rule allows a given property.
    #[inline]
    pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool {
        debug_assert!(
            rule_types.contains(CssRuleType::Keyframe) ||
            rule_types.contains(CssRuleType::Page) ||
            rule_types.contains(CssRuleType::Style) ||
            rule_types.contains(CssRuleType::PositionTry),
            "Declarations are only expected inside a keyframe, page, or style rule."
        );

        static MAP: [u32; property_counts::NON_CUSTOM] = [
            % for property in data.longhands + data.shorthands + data.all_aliases():
            % for name in RULE_VALUES:
            % if property.rule_types_allowed & RULE_VALUES[name] != 0:
            CssRuleType::${name}.bit() |
            % endif
            % endfor
            0,
            % endfor
        ];
        MAP[self.0 as usize] & rule_types.bits() != 0
    }

    pub(super) fn allowed_in(self, context: &ParserContext) -> bool {
        if !self.allowed_in_rule(context.rule_types()) {
            return false;
        }

        self.allowed_in_ignoring_rule_type(context)
    }


    pub(super) fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool {
        // The semantics of these are kinda hard to reason about, what follows
        // is a description of the different combinations that can happen with
        // these three sets.
        //
        // Experimental properties are generally controlled by prefs, but an
        // experimental property explicitly enabled in certain context (UA or
        // chrome sheets) is always usable in the context regardless of the
        // pref value.
        //
        // Non-experimental properties are either normal properties which are
        // usable everywhere, or internal-only properties which are only usable
        // in certain context they are explicitly enabled in.
        if self.enabled_for_all_content() {
            return true;
        }

        ${static_non_custom_property_id_set(
            "ENABLED_IN_UA_SHEETS",
            lambda p: p.explicitly_enabled_in_ua_sheets()
        )}
        ${static_non_custom_property_id_set(
            "ENABLED_IN_CHROME",
            lambda p: p.explicitly_enabled_in_chrome()
        )}

        if context.stylesheet_origin == Origin::UserAgent &&
            ENABLED_IN_UA_SHEETS.contains(self)
        {
            return true
        }

        if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
            return true
        }

        false
    }

    /// The supported types of this property. The return value should be
    /// style_traits::CssType when it can become a bitflags type.
    pub(super) fn supported_types(&self) -> u8 {
        const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [
            % for prop in data.longhands:
                <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,
            % endfor
            % for prop in data.shorthands:
            % if prop.name == "all":
                0, // 'all' accepts no value other than CSS-wide keywords
            % else:
                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,
            % endif
            % endfor
        ];
        SUPPORTED_TYPES[self.0 as usize]
    }

    /// See PropertyId::collect_property_completion_keywords.
    pub(super) fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
        fn do_nothing(_: KeywordsCollectFn) {}
        const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn);
                                  ${len(data.longhands) + len(data.shorthands)}] = [
            % for prop in data.longhands:
                <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,
            % endfor
            % for prop in data.shorthands:
            % if prop.name == "all":
                do_nothing, // 'all' accepts no value other than CSS-wide keywords
            % else:
                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::
                    collect_completion_keywords,
            % endif
            % endfor
        ];
        COLLECT_FUNCTIONS[self.0 as usize](f);
    }
}

<%def name="static_non_custom_property_id_set(name, is_member)">
static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
    <%
        storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
        for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
            if is_member(property):
                storage[int(i / 32)] |= 1 << (i % 32)
    %>
    storage: [${", ".join("0x%x" % word for word in storage)}]
};
</%def>

<%def name="static_longhand_id_set(name, is_member)">
static ${name}: LonghandIdSet = LonghandIdSet {
    <%
        storage = [0] * int((len(data.longhands) - 1 + 32) / 32)
        for i, property in enumerate(data.longhands):
            if is_member(property):
                storage[int(i / 32)] |= 1 << (i % 32)
    %>
    storage: [${", ".join("0x%x" % word for word in storage)}]
};
</%def>

<%
    logical_groups = defaultdict(list)
    for prop in data.longhands:
        if prop.logical_group:
            logical_groups[prop.logical_group].append(prop)

    for group, props in logical_groups.items():
        logical_count = sum(1 for p in props if p.logical)
        if logical_count * 2 != len(props):
            raise RuntimeError("Logical group {} has ".format(group) +
                               "unbalanced logical / physical properties")

    FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
    FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
    MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
    PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
    CUE_RESTRICTIONS = PropertyRestrictions.cue(data)

    def restriction_flags(property):
        name = property.name
        flags = []
        if name in FIRST_LINE_RESTRICTIONS:
            flags.append("APPLIES_TO_FIRST_LINE")
        if name in FIRST_LETTER_RESTRICTIONS:
            flags.append("APPLIES_TO_FIRST_LETTER")
        if name in PLACEHOLDER_RESTRICTIONS:
            flags.append("APPLIES_TO_PLACEHOLDER")
        if name in MARKER_RESTRICTIONS:
            flags.append("APPLIES_TO_MARKER")
        if name in CUE_RESTRICTIONS:
            flags.append("APPLIES_TO_CUE")
        return flags

%>

/// A group for properties which may override each other via logical resolution.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum LogicalGroupId {
    % for i, group in enumerate(logical_groups.keys()):
    /// ${group}
    ${to_camel_case(group)} = ${i},
    % endfor
}

impl LogicalGroupId {
    /// Return the list of physical mapped properties for a given logical group.
    fn physical_properties(self) -> &'static [LonghandId] {
        static PROPS: [[LonghandId; 4]; ${len(logical_groups)}] = [
        % for group, props in logical_groups.items():
        [
            <% physical_props = [p for p in props if p.logical][0].all_physical_mapped_properties(data) %>
            % for phys in physical_props:
            LonghandId::${phys.camel_case},
            % endfor
            % for i in range(len(physical_props), 4):
            LonghandId::${physical_props[0].camel_case},
            % endfor
        ],
        % endfor
        ];
        &PROPS[self as usize]
    }
}

/// A set of logical groups.
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
pub struct LogicalGroupSet {
    storage: [u32; (${len(logical_groups)} - 1 + 32) / 32]
}

impl LogicalGroupSet {
    /// Creates an empty `NonCustomPropertyIdSet`.
    pub fn new() -> Self {
        Self {
            storage: Default::default(),
        }
    }

    /// Return whether the given group is in the set
    #[inline]
    pub fn contains(&self, g: LogicalGroupId) -> bool {
        let bit = g as usize;
        (self.storage[bit / 32] & (1 << (bit % 32))) != 0
    }

    /// Insert a group the set.
    #[inline]
    pub fn insert(&mut self, g: LogicalGroupId) {
        let bit = g as usize;
        self.storage[bit / 32] |= 1 << (bit % 32);
    }
}


#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub(crate) enum PrioritaryPropertyId {
    % for p in data.longhands:
    % if p.is_prioritary():
    ${p.camel_case},
    % endif
    % endfor
}

impl PrioritaryPropertyId {
    #[inline]
    pub fn to_longhand(self) -> LonghandId {
        static PRIORITARY_TO_LONGHAND: [LonghandId; property_counts::PRIORITARY] = [
        % for p in data.longhands:
        % if p.is_prioritary():
            LonghandId::${p.camel_case},
        % endif
        % endfor
        ];
        PRIORITARY_TO_LONGHAND[self as usize]
    }
    #[inline]
    pub fn from_longhand(l: LonghandId) -> Option<Self> {
        static LONGHAND_TO_PRIORITARY: [Option<PrioritaryPropertyId>; ${len(data.longhands)}] = [
        % for p in data.longhands:
        % if p.is_prioritary():
            Some(PrioritaryPropertyId::${p.camel_case}),
        % else:
            None,
        % endif
        % endfor
        ];
        LONGHAND_TO_PRIORITARY[l as usize]
    }
}

impl LonghandIdSet {
    /// The set of non-inherited longhands.
    #[inline]
    pub(super) fn reset() -> &'static Self {
        ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
        &RESET
    }

    #[inline]
    pub(super) fn discrete_animatable() -> &'static Self {
        ${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_type == "discrete")}
        &DISCRETE_ANIMATABLE
    }

    #[inline]
    pub(super) fn logical() -> &'static Self {
        ${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
        &LOGICAL
    }

    /// Returns the set of longhands that are ignored when document colors are
    /// disabled.
    #[inline]
    pub(super) fn ignored_when_colors_disabled() -> &'static Self {
        ${static_longhand_id_set(
            "IGNORED_WHEN_COLORS_DISABLED",
            lambda p: p.ignored_when_colors_disabled
        )}
        &IGNORED_WHEN_COLORS_DISABLED
    }

    /// Only a few properties are allowed to depend on the visited state of
    /// links. When cascading visited styles, we can save time by only
    /// processing these properties.
    pub(super) fn visited_dependent() -> &'static Self {
        ${static_longhand_id_set("VISITED_DEPENDENT", lambda p: p.is_visited_dependent())}
        debug_assert!(Self::late_group().contains_all(&VISITED_DEPENDENT));
        &VISITED_DEPENDENT
    }

    #[inline]
    pub(super) fn prioritary_properties() -> &'static Self {
        ${static_longhand_id_set("PRIORITARY_PROPERTIES", lambda p: p.is_prioritary())}
        &PRIORITARY_PROPERTIES
    }

    #[inline]
    pub(super) fn late_group_only_inherited() -> &'static Self {
        ${static_longhand_id_set("LATE_GROUP_ONLY_INHERITED", lambda p: p.style_struct.inherited and not p.is_prioritary())}
        &LATE_GROUP_ONLY_INHERITED
    }

    #[inline]
    pub(super) fn late_group() -> &'static Self {
        ${static_longhand_id_set("LATE_GROUP", lambda p: not p.is_prioritary())}
        &LATE_GROUP
    }

    /// Returns the set of properties that are declared as having no effect on
    /// Gecko <scrollbar> elements or their descendant scrollbar parts.
    #[cfg(debug_assertions)]
    #[cfg(feature = "gecko")]
    #[inline]
    pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self {
        // data.py asserts that has_no_effect_on_gecko_scrollbars is True or
        // False for properties that are inherited and Gecko pref controlled,
        // and is None for all other properties.
        ${static_longhand_id_set(
            "HAS_NO_EFFECT_ON_SCROLLBARS",
            lambda p: p.has_effect_on_gecko_scrollbars is False
        )}
        &HAS_NO_EFFECT_ON_SCROLLBARS
    }

    /// Returns the set of margin properties, for the purposes of <h1> use counters / warnings.
    #[inline]
    pub fn margin_properties() -> &'static Self {
        ${static_longhand_id_set(
            "MARGIN_PROPERTIES",
            lambda p: p.logical_group == "margin"
        )}
        &MARGIN_PROPERTIES
    }

    /// Returns the set of border properties for the purpose of disabling native
    /// appearance.
    #[inline]
    pub fn border_background_properties() -> &'static Self {
        ${static_longhand_id_set(
            "BORDER_BACKGROUND_PROPERTIES",
            lambda p: (p.logical_group and p.logical_group.startswith("border")) or \
                       p.name in ["background-color", "background-image"]
        )}
        &BORDER_BACKGROUND_PROPERTIES
    }
}

/// An identifier for a given longhand property.
#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(u16)]
pub enum LonghandId {
    % for i, property in enumerate(data.longhands):
        /// ${property.name}
        ${property.camel_case} = ${i},
    % endfor
}

enum LogicalMappingKind {
    Side(LogicalSide),
    Corner(LogicalCorner),
    Axis(LogicalAxis),
}

struct LogicalMappingData {
    group: LogicalGroupId,
    kind: LogicalMappingKind,
}

impl LogicalMappingData {
    fn to_physical(&self, wm: WritingMode) -> LonghandId {
        let index = match self.kind {
            LogicalMappingKind::Side(s) => s.to_physical(wm) as usize,
            LogicalMappingKind::Corner(c) => c.to_physical(wm) as usize,
            LogicalMappingKind::Axis(a) => a.to_physical(wm) as usize,
        };
        self.group.physical_properties()[index]
    }
}

impl LonghandId {
    /// Returns an iterator over all the shorthands that include this longhand.
    pub fn shorthands(self) -> NonCustomPropertyIterator<ShorthandId> {
        // first generate longhand to shorthands lookup map
        //
        // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
        // could potentially do so, which would speed up serialization
        // algorithms and what not, I guess.
        <%
            from functools import cmp_to_key
            longhand_to_shorthand_map = {}
            num_sub_properties = {}
            for shorthand in data.shorthands:
                num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)
                for sub_property in shorthand.sub_properties:
                    if sub_property.ident not in longhand_to_shorthand_map:
                        longhand_to_shorthand_map[sub_property.ident] = []

                    longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)

            def cmp(a, b):
                return (a > b) - (a < b)

            def preferred_order(x, y):
                # Since we want properties in order from most subproperties to least,
                # reverse the arguments to cmp from the expected order.
                result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))
                if result:
                    return result
                # Fall back to lexicographic comparison.
                return cmp(x, y)

            # Sort the lists of shorthand properties according to preferred order:
            # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
            for shorthand_list in longhand_to_shorthand_map.values():
                shorthand_list.sort(key=cmp_to_key(preferred_order))
        %>

        // based on lookup results for each longhand, create result arrays
        static MAP: [&'static [ShorthandId]; property_counts::LONGHANDS] = [
        % for property in data.longhands:
            &[
                % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
                    ShorthandId::${shorthand},
                % endfor
            ],
        % endfor
        ];

        NonCustomPropertyIterator {
            filter: NonCustomPropertyId::from(self).enabled_for_all_content(),
            iter: MAP[self as usize].iter(),
        }
    }

    pub(super) fn parse_value<'i, 't>(
        self,
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<PropertyDeclaration, ParseError<'i>> {
        type ParsePropertyFn = for<'i, 't> fn(
            context: &ParserContext,
            input: &mut Parser<'i, 't>,
        ) -> Result<PropertyDeclaration, ParseError<'i>>;
        static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
        % for property in data.longhands:
            longhands::${property.ident}::parse_declared,
        % endfor
        ];
        (PARSE_PROPERTY[self as usize])(context, input)
    }

    /// Return the relevant data to map a particular logical property into physical.
    fn logical_mapping_data(self) -> Option<<&'static LogicalMappingData> {
        const LOGICAL_MAPPING_DATA: [Option<LogicalMappingData>; ${len(data.longhands)}] = [
            % for prop in data.longhands:
            % if prop.logical:
            Some(LogicalMappingData {
                group: LogicalGroupId::${to_camel_case(prop.logical_group)},
                kind: ${prop.logical_mapping_kind(data)}
            }),
            % else:
            None,
            % endif
            % endfor
        ];
        LOGICAL_MAPPING_DATA[self as usize].as_ref()
    }

    /// If this is a logical property, return the corresponding physical one in the given
    /// writing mode. Otherwise, return unchanged.
    #[inline]
    pub fn to_physical(self, wm: WritingMode) -> Self {
        let Some(data) = self.logical_mapping_data() else { return self };
        data.to_physical(wm)
    }

    /// Return the logical group of this longhand property.
    pub fn logical_group(self) -> Option<LogicalGroupId> {
        const LOGICAL_GROUP_IDS: [Option<LogicalGroupId>; ${len(data.longhands)}] = [
            % for prop in data.longhands:
            % if prop.logical_group:
            Some(LogicalGroupId::${to_camel_case(prop.logical_group)}),
            % else:
            None,
            % endif
            % endfor
        ];
        LOGICAL_GROUP_IDS[self as usize]
    }

    /// Returns PropertyFlags for given longhand property.
    #[inline(always)]
    pub fn flags(self) -> PropertyFlags {
        // TODO(emilio): This can be simplified further as Rust gains more
        // constant expression support.
        const FLAGS: [u16; ${len(data.longhands)}] = [
            % for property in data.longhands:
                % for flag in property.flags + restriction_flags(property):
                    PropertyFlags::${flag}.bits() |
                % endfor
                0,
            % endfor
        ];
        PropertyFlags::from_bits_retain(FLAGS[self as usize])
    }
}

/// An identifier for a given shorthand property.
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
#[repr(u16)]
pub enum ShorthandId {
    % for i, property in enumerate(data.shorthands):
        /// ${property.name}
        ${property.camel_case} = ${i},
    % endfor
}

impl ShorthandId {
    /// Get the longhand ids that form this shorthand.
    pub fn longhands(self) -> NonCustomPropertyIterator<LonghandId> {
        static MAP: [&'static [LonghandId]; property_counts::SHORTHANDS] = [
        % for property in data.shorthands:
            &[
                % for sub in property.sub_properties:
                    LonghandId::${sub.camel_case},
                % endfor
            ],
        % endfor
        ];
        NonCustomPropertyIterator {
            filter: NonCustomPropertyId::from(self).enabled_for_all_content(),
            iter: MAP[self as usize].iter(),
        }
    }

    /// Try to serialize the given declarations as this shorthand.
    ///
    /// Returns an error if writing to the stream fails, or if the declarations
    /// do not map to a shorthand.
    pub fn longhands_to_css(
        self,
        declarations: &[&PropertyDeclaration],
        dest: &mut CssStringWriter,
    ) -> fmt::Result {
        type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
        fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
            // No need to try to serialize the declarations as the 'all'
            // shorthand, since it only accepts CSS-wide keywords (and variable
            // references), which will be handled in
            // get_shorthand_appendable_value.
            Ok(())
        }

        static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
            % for shorthand in data.shorthands:
            % if shorthand.ident == "all":
                all_to_css,
            % else:
                shorthands::${shorthand.ident}::to_css,
            % endif
            % endfor
        ];

        LONGHANDS_TO_CSS[self as usize](declarations, dest)
    }

    /// Returns PropertyFlags for the given shorthand property.
    #[inline]
    pub fn flags(self) -> PropertyFlags {
        const FLAGS: [u16; ${len(data.shorthands)}] = [
            % for property in data.shorthands:
                % for flag in property.flags:
                    PropertyFlags::${flag}.bits() |
                % endfor
                0,
            % endfor
        ];
        PropertyFlags::from_bits_retain(FLAGS[self as usize])
    }

    /// Returns the order in which this property appears relative to other
    /// shorthands in idl-name-sorting order.
    #[inline]
    pub fn idl_name_sort_order(self) -> u32 {
        <%
            from data import to_idl_name
            ordered = {}
            sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
            for order, shorthand in enumerate(sorted_shorthands):
                ordered[shorthand.ident] = order
        %>
        static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [
            % for property in data.shorthands:
            ${ordered[property.ident]},
            % endfor
        ];
        IDL_NAME_SORT_ORDER[self as usize]
    }

    pub(super) fn parse_into<'i, 't>(
        self,
        declarations: &mut SourcePropertyDeclaration,
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<(), ParseError<'i>> {
        type ParseIntoFn = for<'i, 't> fn(
            declarations: &mut SourcePropertyDeclaration,
            context: &ParserContext,
            input: &mut Parser<'i, 't>,
        ) -> Result<(), ParseError<'i>>;

        fn parse_all<'i, 't>(
            _: &mut SourcePropertyDeclaration,
            _: &ParserContext,
            input: &mut Parser<'i, 't>
        ) -> Result<(), ParseError<'i>> {
            // 'all' accepts no value other than CSS-wide keywords
            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }

        static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
            % for shorthand in data.shorthands:
            % if shorthand.ident == "all":
            parse_all,
            % else:
            shorthands::${shorthand.ident}::parse_into,
            % endif
            % endfor
        ];

        (PARSE_INTO[self as usize])(declarations, context, input)
    }
}

/// The counted unknown property list which is used for css use counters.
///
/// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,
/// see https://bugs.llvm.org/show_bug.cgi?id=44228.
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
#[repr(u32)]
pub enum CountedUnknownProperty {
    % for prop in data.counted_unknown_properties:
    /// ${prop.name}
    ${prop.camel_case},
    % endfor
}

impl CountedUnknownProperty {
    /// Parse the counted unknown property, for testing purposes only.
    pub fn parse_for_testing(property_name: &str) -> Option<Self> {
        ascii_case_insensitive_phf_map! {
            unknown_ids -> CountedUnknownProperty = {
                % for property in data.counted_unknown_properties:
                "${property.name}" => CountedUnknownProperty::${property.camel_case},
                % endfor
            }
        }
        unknown_ids::get(property_name).cloned()
    }

    /// Returns the underlying index, used for use counter.
    #[inline]
    pub fn bit(self) -> usize {
        self as usize
    }
}

impl PropertyId {
    /// Returns a given property from the given name, _regardless of whether it
    /// is enabled or not_, or Err(()) for unknown properties.
    pub(super) fn parse_unchecked(
        property_name: &str,
        use_counters: Option< &UseCounters>,
    ) -> Result<Self, ()> {
        // A special id for css use counters. ShorthandAlias is not used in the Servo build.
        // That's why we need to allow dead_code.
        pub enum StaticId {
            NonCustom(NonCustomPropertyId),
            CountedUnknown(CountedUnknownProperty),
        }
        ascii_case_insensitive_phf_map! {
            static_ids -> StaticId = {
                % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
                "${property.name}" => StaticId::NonCustom(NonCustomPropertyId(${i})),
                % endfor
                % for property in data.counted_unknown_properties:
                "${property.name}" => {
                    StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case})
                },
                % endfor
            }
        }

        if let Some(id) = static_ids::get(property_name) {
            return Ok(match *id {
                StaticId::NonCustom(id) => PropertyId::NonCustom(id),
                StaticId::CountedUnknown(unknown_prop) => {
                    if let Some(counters) = use_counters {
                        counters.counted_unknown_properties.record(unknown_prop);
                    }
                    // Always return Err(()) because these aren't valid custom property names.
                    return Err(());
                }
            });
        }

        let name = crate::custom_properties::parse_name(property_name)?;
        Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))
    }
}

impl PropertyDeclaration {
    /// Given a property declaration, return the property declaration id.
    #[inline]
    pub fn id(&self) -> PropertyDeclarationId {
        match *self {
            PropertyDeclaration::Custom(ref declaration) => {
                return PropertyDeclarationId::Custom(&declaration.name)
            }
            PropertyDeclaration::CSSWideKeyword(ref declaration) => {
                return PropertyDeclarationId::Longhand(declaration.id);
            }
            PropertyDeclaration::WithVariables(ref declaration) => {
                return PropertyDeclarationId::Longhand(declaration.id);
            }
            _ => {}
        }
        // This is just fine because PropertyDeclaration and LonghandId
        // have corresponding discriminants.
        let id = unsafe { *(self as *const _ as *const LonghandId) };
        debug_assert_eq!(id, match *self {
            % for property in data.longhands:
            PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},
            % endfor
            _ => id,
        });
        PropertyDeclarationId::Longhand(id)
    }

    /// Given a declaration, convert it into a declaration for a corresponding
    /// physical property.
    #[inline]
    pub fn to_physical(&self, wm: WritingMode) -> Self {
        match *self {
            PropertyDeclaration::WithVariables(VariableDeclaration {
                id,
                ref value,
            }) => {
                return PropertyDeclaration::WithVariables(VariableDeclaration {
                    id: id.to_physical(wm),
                    value: value.clone(),
                })
            }
            PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
                id,
                keyword,
            }) => {
                return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
                    id: id.to_physical(wm),
                    keyword,
                })
            }
            PropertyDeclaration::Custom(..) => return self.clone(),
            % for prop in data.longhands:
            PropertyDeclaration::${prop.camel_case}(..) => {},
            % endfor
        }

        let mut ret = self.clone();

        % for prop in data.longhands:
        % for physical_property in prop.all_physical_mapped_properties(data):
        % if physical_property.specified_type() != prop.specified_type():
            <% raise "Logical property %s should share specified value with physical property %s" % \
                     (prop.name, physical_property.name) %>
        % endif
        % endfor
        % endfor

        unsafe {
            let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);

            debug_assert_eq!(
                PropertyDeclarationId::Longhand(longhand_id),
                ret.id()
            );

            // This is just fine because PropertyDeclaration and LonghandId
            // have corresponding discriminants.
            *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);

            debug_assert_eq!(
                PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),
                ret.id()
            );
        }

        ret
    }

    /// Returns whether or not the property is set by a system font
    pub fn get_system(&self) -> Option<SystemFont> {
        match *self {
            % if engine == "gecko":
            % for prop in SYSTEM_FONT_LONGHANDS:
                PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
                    prop.get_system()
                }
            % endfor
            % endif
            _ => None,
        }
    }
}

#[cfg(feature = "gecko")]
pub use super::gecko::style_structs;

/// The module where all the style structs are defined.
#[cfg(feature = "servo")]
pub mod style_structs {
    use fxhash::FxHasher;
    use super::longhands;
    use std::hash::{Hash, Hasher};
    use crate::values::specified::color::ColorSchemeFlags;

    % for style_struct in data.active_style_structs():
        % if style_struct.name == "Font":
        #[derive(Clone, Debug, MallocSizeOf)]
        #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
        % else:
        #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
        % endif
        /// The ${style_struct.name} style struct.
        pub struct ${style_struct.name} {
            % for longhand in style_struct.longhands:
                % if not longhand.logical:
                    /// The ${longhand.name} computed value.
                    pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
                % endif
            % endfor
            % if style_struct.name == "InheritedText":
                /// The "used" text-decorations that apply to this box.
                ///
                /// FIXME(emilio): This is technically a box-tree concept, and
                /// would be nice to move away from style.
                pub text_decorations_in_effect: crate::values::computed::text::TextDecorationsInEffect,
            % endif
            % if style_struct.name == "Font":
                /// The font hash, used for font caching.
                pub hash: u64,
            % endif
            % if style_struct.name == "Box":
                /// The display value specified by the CSS stylesheets (without any style adjustments),
                /// which is needed for hypothetical layout boxes.
                pub original_display: longhands::display::computed_value::T,
            % endif
        }
        % if style_struct.name == "Font":
        impl PartialEq for Font {
            fn eq(&self, other: &Font) -> bool {
                self.hash == other.hash
                % for longhand in style_struct.longhands:
                    && self.${longhand.ident} == other.${longhand.ident}
                % endfor
            }
        }
        % endif

        impl ${style_struct.name} {
            % for longhand in style_struct.longhands:
                % if not longhand.logical:
                    % if longhand.ident == "display":
                        /// Set `display`.
                        ///
                        /// We need to keep track of the original display for hypothetical boxes,
                        /// so we need to special-case this.
                        #[allow(non_snake_case)]
                        #[inline]
                        pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
                            self.display = v;
                            self.original_display = v;
                        }
                    % else:
                        /// Set ${longhand.name}.
                        #[allow(non_snake_case)]
                        #[inline]
                        pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
                            self.${longhand.ident} = v;
                        }
                    % endif
                    % if longhand.ident == "display":
                        /// Set `display` from other struct.
                        ///
                        /// Same as `set_display` above.
                        /// Thus, we need to special-case this.
                        #[allow(non_snake_case)]
                        #[inline]
                        pub fn copy_display_from(&mut self, other: &Self) {
                            self.display = other.display.clone();
                            self.original_display = other.display.clone();
                        }
                    % else:
                        /// Set ${longhand.name} from other struct.
                        #[allow(non_snake_case)]
                        #[inline]
                        pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {
                            self.${longhand.ident} = other.${longhand.ident}.clone();
                        }
                    % endif
                    /// Reset ${longhand.name} from the initial struct.
                    #[allow(non_snake_case)]
                    #[inline]
                    pub fn reset_${longhand.ident}(&mut self, other: &Self) {
                        self.copy_${longhand.ident}_from(other)
                    }

                    /// Get the computed value for ${longhand.name}.
                    #[allow(non_snake_case)]
                    #[inline]
                    pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
                        self.${longhand.ident}.clone()
                    }
                % endif
                % if longhand.need_index:
                    /// If this longhand is indexed, get the number of elements.
                    #[allow(non_snake_case)]
                    pub fn ${longhand.ident}_count(&self) -> usize {
                        self.${longhand.ident}.0.len()
                    }

                    /// If this longhand is indexed, get the element at given
                    /// index.
                    #[allow(non_snake_case)]
                    pub fn ${longhand.ident}_at(&self, index: usize)
                        -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
                        self.${longhand.ident}.0[index].clone()
                    }
                % endif
            % endfor
            % if style_struct.name == "Border":
                % for side in ["top", "right", "bottom", "left"]:
                    /// Whether the border-${side} property has nonzero width.
                    #[allow(non_snake_case)]
                    pub fn border_${side}_has_nonzero_width(&self) -> bool {
                        use crate::Zero;
                        !self.border_${side}_width.is_zero()
                    }
                % endfor
            % elif style_struct.name == "Font":
                /// Computes a font hash in order to be able to cache fonts
                /// effectively in GFX and layout.
                pub fn compute_font_hash(&mut self) {
                    let mut hasher: FxHasher = Default::default();
                    self.font_weight.hash(&mut hasher);
                    self.font_stretch.hash(&mut hasher);
                    self.font_style.hash(&mut hasher);
                    self.font_family.hash(&mut hasher);
                    self.hash = hasher.finish()
                }
                /// Create a new Font with the initial values of all members.
                pub fn initial_values() -> Self {
                    Self {
                        % for longhand in style_struct.longhands:
                            % if not longhand.logical:
                                ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
                            % endif
                        % endfor
                        hash: 0,
                    }
                 }
            % elif style_struct.name == "InheritedUI":
                /// Returns the ColorSchemeFlags corresponding to the value of `color-scheme`.
                #[inline]
                pub fn color_scheme_bits(&self) -> ColorSchemeFlags {
                    self.color_scheme.bits
                }
            % elif style_struct.name == "Outline":
                /// Whether the outline-width property is non-zero.
                #[inline]
                pub fn outline_has_nonzero_width(&self) -> bool {
                    use crate::Zero;
                    !self.outline_width.is_zero()
                }
            % elif style_struct.name == "Box":
                /// Sets the display property, but without touching original_display,
                /// except when the adjustment comes from root or item display fixups.
                pub fn set_adjusted_display(
                    &mut self,
                    dpy: longhands::display::computed_value::T,
                    is_item_or_root: bool
                ) {
                    self.display = dpy;
                    if is_item_or_root {
                        self.original_display = dpy;
                    }
                }
            % endif
        }

    % endfor
}

% for style_struct in data.active_style_structs():
    impl style_structs::${style_struct.name} {
        % for longhand in style_struct.longhands:
            % if longhand.need_index:
                /// Iterate over the values of ${longhand.name}.
                #[allow(non_snake_case)]
                #[inline]
                pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter {
                    ${longhand.camel_case}Iter {
                        style_struct: self,
                        current: 0,
                        max: self.${longhand.ident}_count(),
                    }
                }

                /// Get a value mod `index` for the property ${longhand.name}.
                #[allow(non_snake_case)]
                #[inline]
                pub fn ${longhand.ident}_mod(&self, index: usize)
                    -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
                    self.${longhand.ident}_at(index % self.${longhand.ident}_count())
                }

                /// Clone the computed value for the property.
                #[allow(non_snake_case)]
                #[inline]
                #[cfg(feature = "gecko")]
                pub fn clone_${longhand.ident}(
                    &self,
                ) -> longhands::${longhand.ident}::computed_value::T {
                    longhands::${longhand.ident}::computed_value::List(
                        self.${longhand.ident}_iter().collect()
                    )
                }
            % endif
        % endfor

        % if style_struct.name == "UI":
            /// Returns whether there is any animation specified with
            /// animation-name other than `none`.
            pub fn specifies_animations(&self) -> bool {
                self.animation_name_iter().any(|name| !name.is_none())
            }

            /// Returns whether there are any transitions specified.
            #[cfg(feature = "servo")]
            pub fn specifies_transitions(&self) -> bool {
                (0..self.transition_property_count()).any(|index| {
                    let combined_duration =
                        self.transition_duration_mod(index).seconds().max(0.) +
                        self.transition_delay_mod(index).seconds();
                    combined_duration > 0.
                })
            }

            /// Returns whether animation-timeline is initial value. We need this information to
            /// resolve animation-duration.
            #[cfg(feature = "servo")]
            pub fn has_initial_animation_timeline(&self) -> bool {
                self.animation_timeline_count() == 1 && self.animation_timeline_at(0).is_auto()
            }

            /// Returns whether there is any named progress timeline specified with
            /// scroll-timeline-name other than `none`.
            #[cfg(feature = "gecko")]
            pub fn specifies_scroll_timelines(&self) -> bool {
                self.scroll_timeline_name_iter().any(|name| !name.is_none())
            }

            /// Returns whether there is any named progress timeline specified with
            /// view-timeline-name other than `none`.
            #[cfg(feature = "gecko")]
            pub fn specifies_view_timelines(&self) -> bool {
                self.view_timeline_name_iter().any(|name| !name.is_none())
            }

            /// Returns true if animation properties are equal between styles, but without
            /// considering keyframe data and animation-timeline.
            #[cfg(feature = "servo")]
            pub fn animations_equals(&self, other: &Self) -> bool {
                self.animation_name_iter().eq(other.animation_name_iter()) &&
                self.animation_delay_iter().eq(other.animation_delay_iter()) &&
                self.animation_direction_iter().eq(other.animation_direction_iter()) &&
                self.animation_duration_iter().eq(other.animation_duration_iter()) &&
                self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) &&
                self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) &&
                self.animation_play_state_iter().eq(other.animation_play_state_iter()) &&
                self.animation_timing_function_iter().eq(other.animation_timing_function_iter())
            }

        % elif style_struct.name == "Column":
            /// Whether this is a multicol style.
            #[cfg(feature = "servo")]
            pub fn is_multicol(&self) -> bool {
                !self.column_width.is_auto() || !self.column_count.is_auto()
            }
        % endif
    }

    % for longhand in style_struct.longhands:
        % if longhand.need_index:
            /// An iterator over the values of the ${longhand.name} properties.
            pub struct ${longhand.camel_case}Iter<'a> {
                style_struct: &'a style_structs::${style_struct.name},
                current: usize,
                max: usize,
            }

            impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {
                type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;

                fn next(&mut self) -> Option<Self::Item> {
                    self.current += 1;
                    if self.current <= self.max {
                        Some(self.style_struct.${longhand.ident}_at(self.current - 1))
                    } else {
                        None
                    }
                }
            }
        % endif
    % endfor
% endfor


#[cfg(feature = "gecko")]
pub use super::gecko::{ComputedValues, ComputedValuesInner};

#[cfg(feature = "servo")]
#[cfg_attr(feature = "servo", derive(Clone, Debug))]
/// Actual data of ComputedValues, to match up with Gecko
pub struct ComputedValuesInner {
    % for style_struct in data.active_style_structs():
        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
    % endfor
    custom_properties: crate::custom_properties::ComputedCustomProperties,

    /// The writing mode of this computed values struct.
    pub writing_mode: WritingMode,

    /// The effective zoom value.
    pub effective_zoom: computed::Zoom,

    /// A set of flags we use to store misc information regarding this style.
    pub flags: ComputedValueFlags,

    /// The rule node representing the ordered list of rules matched for this
    /// node.  Can be None for default values and text nodes.  This is
    /// essentially an optimization to avoid referencing the root rule node.
    pub rules: Option<StrongRuleNode>,

    /// The element's computed values if visited, only computed if there's a
    /// relevant link for this element. A element's "relevant link" is the
    /// element being matched if it is a link or the nearest ancestor link.
    visited_style: Option<Arc<ComputedValues>>,
}

/// The struct that Servo uses to represent computed values.
///
/// This struct contains an immutable atomically-reference-counted pointer to
/// every kind of style struct.
///
/// When needed, the structs may be copied in order to get mutated.
#[cfg(feature = "servo")]
#[cfg_attr(feature = "servo", derive(Clone, Debug))]
pub struct ComputedValues {
    /// The actual computed values
    ///
    /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas
    /// ComputedValuesInner is the core set of computed values.
    ///
    /// We maintain this distinction in servo to reduce the amount of special
    /// casing.
    inner: ComputedValuesInner,

    /// The pseudo-element that we're using.
    pseudo: Option<PseudoElement>,
}

impl ComputedValues {
    /// Returns the pseudo-element that this style represents.
    #[cfg(feature = "servo")]
    pub fn pseudo(&self) -> Option<<&PseudoElement> {
        self.pseudo.as_ref()
    }

--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.57 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge