Bug 1575062 - Support css use counters for unimplemented properties. r=emilio
authorBoris Chiou <boris.chiou@gmail.com>
Thu, 29 Aug 2019 23:40:13 +0000
changeset 551333 f529796a87c2e6ef2db08d7d8348500901ec4da9
parent 551332 514f732deb3081429743d8c58902c7f94921f728
child 551334 c2a5945163a9c36f99da5aa9fa7b45b551731408
child 551525 92c38343d239347166b17935daba3078876dc5be
push id11865
push userbtara@mozilla.com
push dateMon, 02 Sep 2019 08:54:37 +0000
treeherdermozilla-beta@37f59c4671b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1575062
milestone70.0a1
first release with
nightly linux32
f529796a87c2 / 70.0a1 / 20190830093857 / files
nightly linux64
f529796a87c2 / 70.0a1 / 20190830093857 / files
nightly mac
f529796a87c2 / 70.0a1 / 20190830093857 / files
nightly win32
f529796a87c2 / 70.0a1 / 20190830093857 / files
nightly win64
f529796a87c2 / 70.0a1 / 20190830093857 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1575062 - Support css use counters for unimplemented properties. r=emilio For developing properties, we will handle them in an other bug. Besides, I use an iframe for the test because we create a use counter in the constructor of Document, which use the prefs to decide what kind of properties we want to record. So, in the test, we have to reload iframe to make sure we re-create the document, so does the use counter, to make sure the prefs work properly. The two prefs affect the css use counters: 1. layout.css.use-counters.enabled: Allocate use counters, and record non-custom properties. 2. layout.css.use-counters-unimplemented.enabled: Record all unimplmented properties into the use counters. If we disable layout.css.use-counters.enblaed, we don't create use counters object, so layout.css.use-counters-unimplemented.enabled doesn't work, either. Differential Revision: https://phabricator.services.mozilla.com/D43860
layout/style/test/test_use_counters.html
modules/libpref/init/StaticPrefList.yaml
servo/components/style/properties/counted_unknown_properties.py
servo/components/style/properties/data.py
servo/components/style/properties/properties.mako.rs
servo/components/style/use_counters/mod.rs
servo/ports/geckolib/glue.rs
--- a/layout/style/test/test_use_counters.html
+++ b/layout/style/test/test_use_counters.html
@@ -1,66 +1,138 @@
 <!doctype html>
 <title>Test for Bug 1425700: CSS properties use-counters</title>
 <link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <body>
+<iframe id="iframe"></iframe>
 <script>
-const utils = SpecialPowers.getDOMWindowUtils(window);
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv(
-  { "set": [["layout.css.use-counters.enabled", true ]]},
-  run,
-);
 
-function assert_recorded(recorded, properties) {
+function iframe_reload() {
+  return new Promise(resolve => {
+    iframe.addEventListener("load", _ => resolve());
+    iframe.contentWindow.location.reload();
+  });
+}
+
+function assert_recorded(win, recorded, properties, desc) {
+  const utils = SpecialPowers.getDOMWindowUtils(win);
   for (const prop of properties) {
     try {
-      is(utils.isCssPropertyRecordedInUseCounter(prop), recorded, prop)
+      is(utils.isCssPropertyRecordedInUseCounter(prop), recorded,
+         `${desc} - ${prop}`)
     } catch(ex) {
       ok(false, "Threw: " + prop);
     }
   }
 }
 
-function run() {
+// Test implemented properties (i.e. non custom properties).
+add_task(async () => {
+  await SpecialPowers.pushPrefEnv({
+    "set": [
+      ["layout.css.use-counters.enabled", true],
+      ["layout.css.use-counters-unimplemented.enabled", false]
+    ]
+  });
+
+  let iframe = document.getElementById("iframe");
+  // Reload iframe to make sure we re-create its document, which makes the
+  // preference update meaningful.
+  await iframe_reload();
+
   const style = document.createElement('style');
   style.textContent = `
     * {
       grid-gap: 1px; /* shorthand alias */
       -webkit-background-size: 100px 100px; /* longhand alias */
       transform-origin: top left; /* longhand */
       background: green; /* shorthand */
+      zoom: 1.0; /* counted unknown */
     }
   `;
 
-  document.body.appendChild(style);
+  iframe.contentDocument.body.appendChild(style);
 
-  assert_recorded(true, [
+  const win = iframe.contentWindow;
+  assert_recorded(win, true, [
     "grid-gap",
     "-webkit-background-size",
     "transform-origin",
     "background"
-  ]);
+  ], "Test non-custom properties");
 
   // Should only record the aliases, not the non-aliased property.
   // Should only record shorthands, not the longhands it expands to.
-  assert_recorded(false, [
+  // Should not record counted unknown property.
+  assert_recorded(win, false, [
     "gap",
     "background-size",
     "-moz-transform-origin",
     "-webkit-transform-origin",
-    "background-color"
-  ]);
+    "background-color",
+    "zoom"
+  ], "Test non-custom properties");
 
   // TODO(emilio): Make work (and test) inline style and maybe even CSSOM and
   // such?
   //
   // Make sure that something on the lines of the following passes:
   //
   //   element.style.webkitTransform = "rotate(1deg)"
   //   assert_recorded(true, ["-webkit-transform"]);
   //   assert_recorded(false, ["transform"]);
   //
-  SimpleTest.finish();
-}
+  style.remove();
+  await SpecialPowers.flushPrefEnv();
+});
+
+// Test unimplemented properties.
+add_task(async () => {
+  await SpecialPowers.pushPrefEnv({
+    "set": [
+      ["layout.css.use-counters.enabled", true],
+      ["layout.css.use-counters-unimplemented.enabled", true]
+    ]
+  });
+
+  let iframe = document.getElementById("iframe");
+  // Reload iframe to make sure we re-create its document, which makes the
+  // preference update meaningful.
+  await iframe_reload();
+
+  const style = document.createElement('style');
+  style.textContent = `
+    * {
+      grid-gap: 1px; /* shorthand alias */
+      -webkit-background-size: 100px 100px; /* longhand alias */
+      transform-origin: top left; /* longhand */
+      background: green; /* shorthand */
+      -webkit-font-smoothing: auto; /* counted unknown */
+      zoom: 0.5; /* counted unknown */
+    }
+  `;
+
+  iframe.contentDocument.body.appendChild(style);
+
+  const win = iframe.contentWindow;
+  assert_recorded(win, true, [
+    "grid-gap",
+    "-webkit-background-size",
+    "transform-origin",
+    "background",
+    "-webkit-font-smoothing",
+    "zoom",
+  ], "Test unimplemented properties");
+
+  // Shouldn't record counted unknown properties which doesn't show up.
+  assert_recorded(win, false, [
+    "size",
+    "speak",
+  ], "Test unimplemented properties");
+
+  // TODO: Make work (and test) inline style and CSSOM.
+  style.remove();
+  await SpecialPowers.flushPrefEnv();
+});
+
 </script>
 </body>
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -4760,16 +4760,23 @@
   rust: true
 
 # Are style system use counters enabled?
 - name: layout.css.use-counters.enabled
   type: bool
   value: @IS_NOT_RELEASE_OR_BETA@
   mirror: always
 
+# Are counters for unimplemented CSS properties enabled?
+- name: layout.css.use-counters-unimplemented.enabled
+  type: RelaxedAtomicBool
+  value: @IS_NOT_RELEASE_OR_BETA@
+  mirror: always
+  rust: true
+
 # Should the :visited selector ever match (otherwise :link matches instead)?
 - name: layout.css.visited_links_enabled
   type: bool
   value: true
   mirror: always
 
 # Is the '-webkit-appearance' alias for '-moz-appearance' enabled?
 - name: layout.css.webkit-appearance.enabled
new file mode 100644
--- /dev/null
+++ b/servo/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",
+]
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -1,13 +1,14 @@
 # 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/.
 
 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"]
 PHYSICAL_SIZES = ["width", "height"]
 LOGICAL_SIZES = ["block-size", "inline-size"]
 PHYSICAL_CORNERS = ["top-left", "top-right", "bottom-right", "bottom-left"]
 LOGICAL_CORNERS = ["start-start", "start-end", "end-start", "end-end"]
 PHYSICAL_AXES = ["x", "y"]
@@ -569,16 +570,17 @@ class PropertiesData(object):
         self.current_style_struct = None
         self.longhands = []
         self.longhands_by_name = {}
         self.longhands_by_logical_group = {}
         self.longhand_aliases = []
         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)
         self.style_structs.append(style_struct)
         self.current_style_struct = style_struct
 
     def active_style_structs(self):
         return [s for s in self.style_structs if s.additional_methods or s.longhands]
@@ -789,8 +791,14 @@ class PropertyRestrictions:
             # FIXME(emilio): background-blend-mode should be part of the
             # background shorthand, and get reset, per
             # https://drafts.fxtf.org/compositing/#background-blend-mode
             "background-blend-mode",
         ] + PropertyRestrictions.shorthand(data, "text-decoration")
           + 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)
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -33,16 +33,17 @@ use crate::parser::ParserContext;
 use crate::properties::longhands::system_font::SystemFont;
 use crate::selector_parser::PseudoElement;
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")] use servo_config::prefs;
 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;
 use crate::values::serialize_atom_name;
 use crate::rule_tree::StrongRuleNode;
 use crate::Zero;
 use self::computed_value_flags::*;
 use crate::str::{CssString, CssStringBorrow, CssStringWriter};
@@ -422,16 +423,19 @@ pub mod animated_properties {
 /// A longhand or shorthand property.
 #[derive(Clone, Copy, Debug)]
 pub struct NonCustomPropertyId(usize);
 
 /// The length of all the non-custom properties.
 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() {
     % 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
@@ -1783,76 +1787,149 @@ impl ToCss for PropertyId {
             PropertyId::Custom(ref name) => {
                 dest.write_str("--")?;
                 serialize_atom_name(name, dest)
             }
         }
     }
 }
 
+/// 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<Self> {
+        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]
     pub fn longhand_id(&self) -> Option<LonghandId> {
         Some(match *self {
             PropertyId::Longhand(id) => id,
             PropertyId::LonghandAlias(id, _) => id,
             _ => return None,
         })
     }
 
     /// Returns a given property from the string `s`.
     ///
-    /// Returns Err(()) for unknown non-custom properties.
-    fn parse_unchecked(property_name: &str) -> Result<Self, ()> {
+    /// Returns Err(()) for unknown properties.
+    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.
+        #[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},
                     )
                 },
                 % 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)?;
         Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))
     }
 
     /// Parses a property name, and returns an error if it's unknown or isn't
     /// enabled for all content.
     #[inline]
     pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
-        let id = Self::parse_unchecked(name)?;
+        let id = Self::parse_unchecked(name, None)?;
 
         if !id.enabled_for_all_content() {
             return Err(());
         }
 
         Ok(id)
     }
 
 
     /// Parses a property name, and returns an error if it's unknown or isn't
     /// allowed in this context.
     #[inline]
     pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
-        let id = Self::parse_unchecked(name)?;
+        let id = Self::parse_unchecked(name, context.use_counters)?;
 
         if !id.allowed_in(context) {
             return Err(());
         }
 
         Ok(id)
     }
 
@@ -1860,17 +1937,17 @@ impl PropertyId {
     /// allowed in this context, ignoring the rule_type checks.
     ///
     /// This is useful for parsing stuff from CSS values, for example.
     #[inline]
     pub fn parse_ignoring_rule_type(
         name: &str,
         context: &ParserContext,
     ) -> Result<Self, ()> {
-        let id = Self::parse_unchecked(name)?;
+        let id = Self::parse_unchecked(name, None)?;
 
         if !id.allowed_in_ignoring_rule_type(context) {
             return Err(());
         }
 
         Ok(id)
     }
 
--- a/servo/components/style/use_counters/mod.rs
+++ b/servo/components/style/use_counters/mod.rs
@@ -1,83 +1,104 @@
 /* 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/. */
 
 //! Various stuff for CSS property use counters.
 
 #[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;
 
 #[cfg(target_pointer_width = "64")]
 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<usize>; (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<usize>; (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 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)
+        }
 
-    /// 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)
-    }
+        /// 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
+        }
 
-    /// 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
-    }
+        /// 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.
 #[derive(Default)]
 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 {
     /// Merge the use counters.
     ///
     /// Used for parallel parsing, where we parse off-main-thread.
     #[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);
     }
 }
 
 #[cfg(feature = "gecko")]
 unsafe impl HasFFI for UseCounters {
     type FFIType = crate::gecko_bindings::structs::StyleUseCounters;
 }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -98,17 +98,17 @@ use style::gecko_bindings::sugar::owners
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::global_style_data::{GlobalStyleData, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
 use style::invalidation::element::restyle_hints::RestyleHint;
 use style::media_queries::MediaList;
 use style::parser::{self, Parse, ParserContext};
 use style::profiler_label;
 use style::properties::animated_properties::{AnimationValue, AnimationValueMap};
 use style::properties::{parse_one_declaration_into, parse_style_attribute};
-use style::properties::{ComputedValues, Importance, NonCustomPropertyId};
+use style::properties::{ComputedValues, CountedUnknownProperty, Importance, NonCustomPropertyId};
 use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
 use style::properties::{PropertyDeclarationId, ShorthandId};
 use style::properties::{SourcePropertyDeclaration, StyleBuilder, UnparsedValue};
 use style::rule_cache::RuleCacheConditions;
 use style::rule_tree::{CascadeLevel, StrongRuleNode};
 use style::selector_parser::PseudoElementCascadeType;
 use style::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard};
 use style::string_cache::{Atom, WeakAtom};
@@ -6548,23 +6548,34 @@ pub unsafe extern "C" fn Servo_UseCounte
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
     use_counters: &UseCounters,
     property: *const nsACString,
     known_prop: *mut bool,
 ) -> bool {
-    let prop_id = parse_enabled_property_name!(property, known_prop, false);
-    let non_custom_id = match prop_id.non_custom_id() {
-        Some(id) => id,
-        None => return false,
+    *known_prop = false;
+    let prop_name = property.as_ref().unwrap().as_str_unchecked();
+    let non_custom_id = match PropertyId::parse_enabled_for_all_content(prop_name) {
+        Ok(p) => {
+            *known_prop = true;
+            p.non_custom_id()
+        }
+        Err(..) => None,
     };
 
-    use_counters.non_custom_properties.recorded(non_custom_id)
+    if let Some(id) = non_custom_id {
+        return use_counters.non_custom_properties.recorded(id);
+    }
+
+    CountedUnknownProperty::parse_for_test(prop_name).map_or(false, |p| {
+        *known_prop = true;
+        use_counters.counted_unknown_properties.recorded(p)
+    })
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Create(
     buffer: *mut u8,
     len: usize,
 ) -> *mut RawServoSharedMemoryBuilder {
     let mut builder = Box::new(SharedMemoryBuilder::new(buffer, len));