diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py new file mode 100644 index 000000000000..628d7463ded8 --- /dev/null +++ b/components/style/properties/counted_unknown_properties.py @@ -0,0 +1,128 @@ +# 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 http://mozilla.org/MPL/2.0/. + +# The following properties are under development, so they are not in this list. +# FIXME: We should handle the developing properties properly by Bug 1577358: +# "backdrop-filter", +# "text-decoration-skip-ink", +# "column-span", +# "offset-distance", +# "offset-path", +# "offset-rotate" +COUNTED_UNKNOWN_PROPERTIES = [ + "-webkit-font-smoothing", + "zoom", + "-webkit-tap-highlight-color", + "speak", + "text-size-adjust", + "-webkit-font-feature-settings", + "-webkit-user-drag", + "size", + "-webkit-clip-path", + "orphans", + "widows", + "-webkit-user-modify", + "-webkit-margin-before", + "-webkit-margin-after", + "tab-size", + "-webkit-margin-start", + "-webkit-column-break-inside", + "-webkit-padding-start", + "-webkit-margin-end", + "-webkit-box-reflect", + "-webkit-print-color-adjust", + "-webkit-mask-box-image", + "-webkit-line-break", + "-webkit-text-security", + "alignment-baseline", + "-webkit-writing-mode", + "baseline-shift", + "-webkit-hyphenate-character", + "page", + "text-underline-position", + "-webkit-highlight", + "background-repeat-x", + "-webkit-padding-end", + "background-repeat-y", + "-webkit-text-emphasis-color", + "-webkit-margin-top-collapse", + "-webkit-rtl-ordering", + "-webkit-padding-before", + "-webkit-text-decorations-in-effect", + "-webkit-border-vertical-spacing", + "-webkit-locale", + "-webkit-padding-after", + "-webkit-border-horizontal-spacing", + "color-rendering", + "-webkit-column-break-before", + "-webkit-transform-origin-x", + "-webkit-transform-origin-y", + "-webkit-text-emphasis-position", + "buffered-rendering", + "-webkit-text-orientation", + "-webkit-text-combine", + "-webkit-text-emphasis-style", + "-webkit-text-emphasis", + "d", + "-webkit-mask-box-image-width", + "-webkit-mask-box-image-source", + "-webkit-mask-box-image-outset", + "-webkit-mask-box-image-slice", + "-webkit-mask-box-image-repeat", + "-webkit-margin-after-collapse", + "-webkit-border-before-color", + "-webkit-border-before-width", + "-webkit-perspective-origin-x", + "-webkit-perspective-origin-y", + "-webkit-margin-before-collapse", + "-webkit-border-before-style", + "scroll-snap-stop", + "-webkit-margin-bottom-collapse", + "-webkit-ruby-position", + "-webkit-column-break-after", + "-webkit-margin-collapse", + "offset", + "-webkit-border-before", + "-webkit-border-end", + "-webkit-border-after", + "-webkit-border-start", + "-webkit-min-logical-width", + "-webkit-logical-height", + "-webkit-transform-origin-z", + "-webkit-font-size-delta", + "-webkit-logical-width", + "-webkit-max-logical-width", + "-webkit-min-logical-height", + "-webkit-max-logical-height", + "-webkit-border-end-color", + "-webkit-border-end-width", + "-webkit-border-start-color", + "-webkit-border-start-width", + "-webkit-border-after-color", + "-webkit-border-after-width", + "-webkit-border-end-style", + "-webkit-border-after-style", + "-webkit-border-start-style", + "-webkit-mask-repeat-x", + "-webkit-mask-repeat-y", + "user-zoom", + "min-zoom", + "-webkit-box-decoration-break", + "orientation", + "max-zoom", + "-webkit-app-region", + "-webkit-column-rule", + "-webkit-column-span", + "-webkit-column-gap", + "-webkit-shape-outside", + "-webkit-column-rule-width", + "-webkit-column-count", + "-webkit-opacity", + "-webkit-column-width", + "-webkit-shape-image-threshold", + "-webkit-column-rule-style", + "-webkit-columns", + "-webkit-column-rule-color", + "-webkit-shape-margin", +] diff --git a/components/style/properties/data.py b/components/style/properties/data.py index d1e78e6648c2..7c2330e47d8d 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -3,6 +3,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. import re +from counted_unknown_properties import COUNTED_UNKNOWN_PROPERTIES PHYSICAL_SIDES = ["top", "right", "bottom", "left"] LOGICAL_SIDES = ["block-start", "block-end", "inline-start", "inline-end"] @@ -574,6 +575,7 @@ def __init__(self, engine): self.shorthands = [] self.shorthands_by_name = {} self.shorthand_aliases = [] + self.counted_unknown_properties = [CountedUnknownProperty(p) for p in COUNTED_UNKNOWN_PROPERTIES] def new_style_struct(self, *args, **kwargs): style_struct = StyleStruct(*args, **kwargs) @@ -794,3 +796,9 @@ def cue(data): + PropertyRestrictions.shorthand(data, "background") + PropertyRestrictions.shorthand(data, "outline") + PropertyRestrictions.shorthand(data, "font")) + +class CountedUnknownProperty: + def __init__(self, name): + self.name = name + self.ident = to_rust_ident(name) + self.camel_case = to_camel_case(self.ident) diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 72ae381f5d9f..a1b4cedcfed5 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -38,6 +38,7 @@ use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use to_shmem::impl_trivial_to_shmem; use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; +use crate::use_counters::UseCounters; use crate::values::generics::text::LineHeight; use crate::values::{computed, resolved}; use crate::values::computed::NonNegativeLength; @@ -427,6 +428,9 @@ pub struct NonCustomPropertyId(usize); pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize = ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}; +/// The length of all counted unknown properties. +pub const COUNTED_UNKNOWN_PROPERTY_COUNT: usize = ${len(data.counted_unknown_properties)}; + % if engine == "gecko": #[allow(dead_code)] unsafe fn static_assert_nscsspropertyid() { @@ -1788,6 +1792,45 @@ impl ToCss for PropertyId { } } +/// The counted unknown property list which is used for css use counters. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(u8)] +pub enum CountedUnknownProperty { + % for prop in data.counted_unknown_properties: + /// ${prop.name} + ${prop.camel_case}, + % endfor +} + +impl CountedUnknownProperty { + /// Parse the counted unknown property. + pub fn parse_for_test(property_name: &str) -> Option { + ascii_case_insensitive_phf_map! { + unknown_id -> CountedUnknownProperty = { + % for property in data.counted_unknown_properties: + "${property.name}" => CountedUnknownProperty::${property.camel_case}, + % endfor + } + } + unknown_id(property_name).cloned() + } + + /// Returns the underlying index, used for use counter. + pub fn bit(self) -> usize { + self as usize + } +} + +#[cfg(feature = "gecko")] +fn is_counted_unknown_use_counters_enabled() -> bool { + static_prefs::pref!("layout.css.use-counters-unimplemented.enabled") +} + +#[cfg(feature = "servo")] +fn is_counted_unknown_use_counters_enabled() -> bool { + false +} + impl PropertyId { /// Return the longhand id that this property id represents. #[inline] @@ -1801,16 +1844,30 @@ impl PropertyId { /// Returns a given property from the string `s`. /// - /// Returns Err(()) for unknown non-custom properties. - fn parse_unchecked(property_name: &str) -> Result { + /// Returns Err(()) for unknown properties. + fn parse_unchecked( + property_name: &str, + use_counters: Option< &UseCounters>, + ) -> Result { + // A special id for css use counters. + // ShorthandAlias is not used in the Servo build. + // That's why we need to allow dead_code. + #[allow(dead_code)] + pub enum StaticId { + Longhand(LonghandId), + Shorthand(ShorthandId), + LonghandAlias(LonghandId, AliasId), + ShorthandAlias(ShorthandId, AliasId), + CountedUnknown(CountedUnknownProperty), + } ascii_case_insensitive_phf_map! { - property_id -> PropertyId = { + static_id -> StaticId = { % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]: % for property in properties: - "${property.name}" => PropertyId::${kind}(${kind}Id::${property.camel_case}), + "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}), % for alias in property.alias: "${alias.name}" => { - PropertyId::${kind}Alias( + StaticId::${kind}Alias( ${kind}Id::${property.camel_case}, AliasId::${alias.camel_case}, ) @@ -1818,11 +1875,31 @@ impl PropertyId { % endfor % endfor % endfor + % for property in data.counted_unknown_properties: + "${property.name}" => { + StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case}) + }, + % endfor } } - if let Some(id) = property_id(property_name) { - return Ok(id.clone()) + if let Some(id) = static_id(property_name) { + return Ok(match *id { + StaticId::Longhand(id) => PropertyId::Longhand(id), + StaticId::Shorthand(id) => PropertyId::Shorthand(id), + StaticId::LonghandAlias(id, alias) => PropertyId::LonghandAlias(id, alias), + StaticId::ShorthandAlias(id, alias) => PropertyId::ShorthandAlias(id, alias), + StaticId::CountedUnknown(unknown_prop) => { + if is_counted_unknown_use_counters_enabled() { + 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)?; @@ -1833,7 +1910,7 @@ impl PropertyId { /// enabled for all content. #[inline] pub fn parse_enabled_for_all_content(name: &str) -> Result { - let id = Self::parse_unchecked(name)?; + let id = Self::parse_unchecked(name, None)?; if !id.enabled_for_all_content() { return Err(()); @@ -1847,7 +1924,7 @@ impl PropertyId { /// allowed in this context. #[inline] pub fn parse(name: &str, context: &ParserContext) -> Result { - let id = Self::parse_unchecked(name)?; + let id = Self::parse_unchecked(name, context.use_counters)?; if !id.allowed_in(context) { return Err(()); @@ -1865,7 +1942,7 @@ impl PropertyId { name: &str, context: &ParserContext, ) -> Result { - let id = Self::parse_unchecked(name)?; + let id = Self::parse_unchecked(name, None)?; if !id.allowed_in_ignoring_rule_type(context) { return Err(()); diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs index c9a4b449ea03..bb8e92594a63 100644 --- a/components/style/use_counters/mod.rs +++ b/components/style/use_counters/mod.rs @@ -6,6 +6,7 @@ #[cfg(feature = "gecko")] use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; +use crate::properties::{CountedUnknownProperty, COUNTED_UNKNOWN_PROPERTY_COUNT}; use crate::properties::{NonCustomPropertyId, NON_CUSTOM_PROPERTY_ID_COUNT}; use std::cell::Cell; @@ -15,46 +16,62 @@ const BITS_PER_ENTRY: usize = 64; #[cfg(target_pointer_width = "32")] const BITS_PER_ENTRY: usize = 32; +/// One bit per each non-custom CSS property. +#[derive(Default)] +pub struct CountedUnknownPropertyUseCounters { + storage: [Cell; (COUNTED_UNKNOWN_PROPERTY_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY], +} + /// One bit per each non-custom CSS property. #[derive(Default)] pub struct NonCustomPropertyUseCounters { storage: [Cell; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY], } -impl NonCustomPropertyUseCounters { - /// Returns the bucket a given property belongs in, and the bitmask for that - /// property. - #[inline(always)] - fn bucket_and_pattern(id: NonCustomPropertyId) -> (usize, usize) { - let bit = id.bit(); - let bucket = bit / BITS_PER_ENTRY; - let bit_in_bucket = bit % BITS_PER_ENTRY; - (bucket, 1 << bit_in_bucket) - } +macro_rules! property_use_counters_methods { + ($id: ident) => { + /// Returns the bucket a given property belongs in, and the bitmask for that + /// property. + #[inline(always)] + fn bucket_and_pattern(id: $id) -> (usize, usize) { + let bit = id.bit(); + let bucket = bit / BITS_PER_ENTRY; + let bit_in_bucket = bit % BITS_PER_ENTRY; + (bucket, 1 << bit_in_bucket) + } - /// Record that a given non-custom property ID has been parsed. - #[inline] - pub fn record(&self, id: NonCustomPropertyId) { - let (bucket, pattern) = Self::bucket_and_pattern(id); - let bucket = &self.storage[bucket]; - bucket.set(bucket.get() | pattern) - } + /// Record that a given property ID has been parsed. + #[inline] + pub fn record(&self, id: $id) { + let (bucket, pattern) = Self::bucket_and_pattern(id); + let bucket = &self.storage[bucket]; + bucket.set(bucket.get() | pattern) + } - /// Returns whether a given non-custom property ID has been recorded - /// earlier. - #[inline] - pub fn recorded(&self, id: NonCustomPropertyId) -> bool { - let (bucket, pattern) = Self::bucket_and_pattern(id); - self.storage[bucket].get() & pattern != 0 - } + /// Returns whether a given property ID has been recorded + /// earlier. + #[inline] + pub fn recorded(&self, id: $id) -> bool { + let (bucket, pattern) = Self::bucket_and_pattern(id); + self.storage[bucket].get() & pattern != 0 + } - /// Merge `other` into `self`. - #[inline] - fn merge(&self, other: &Self) { - for (bucket, other_bucket) in self.storage.iter().zip(other.storage.iter()) { - bucket.set(bucket.get() | other_bucket.get()) + /// Merge `other` into `self`. + #[inline] + fn merge(&self, other: &Self) { + for (bucket, other_bucket) in self.storage.iter().zip(other.storage.iter()) { + bucket.set(bucket.get() | other_bucket.get()) + } } - } + }; +} + +impl CountedUnknownPropertyUseCounters { + property_use_counters_methods!(CountedUnknownProperty); +} + +impl NonCustomPropertyUseCounters { + property_use_counters_methods!(NonCustomPropertyId); } /// The use-counter data related to a given document we want to store. @@ -63,6 +80,8 @@ pub struct UseCounters { /// The counters for non-custom properties that have been parsed in the /// document's stylesheets. pub non_custom_properties: NonCustomPropertyUseCounters, + /// The counters for css properties which we haven't implemented yet. + pub counted_unknown_properties: CountedUnknownPropertyUseCounters, } impl UseCounters { @@ -72,7 +91,9 @@ impl UseCounters { #[inline] pub fn merge(&self, other: &Self) { self.non_custom_properties - .merge(&other.non_custom_properties) + .merge(&other.non_custom_properties); + self.counted_unknown_properties + .merge(&other.counted_unknown_properties); } }