Bug 1425700 - Hook the use counters into StyleSheet parsing. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 20 Aug 2018 19:03:11 +0200
changeset 491366 5449572186472bfc8ed0a48e6b08e6bc4e2424a6
parent 491365 8efef28be72c1ab7e433248924b9459beb2e1b33
child 491367 bc3f9356906bd462f3941e2ec6510a145aa65b2d
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1425700
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1425700 - Hook the use counters into StyleSheet parsing. r=heycam Still not hooked into telemetry, I talked with :janerik and :gfritzsche about that, but test incoming! This intentionally doesn't handle CSSOM and such for now, will file followups for those, though should be trivial. I want to unify / clean up how we do the use counters and the error reporting stuff for CSSOM, since the current function call still shows up in profiles, but that should be a follow-up. Differential Revision: https://phabricator.services.mozilla.com/D3828
dom/base/nsDocument.cpp
dom/base/nsIDocument.h
layout/style/ServoArcTypeList.h
layout/style/ServoBindingList.h
layout/style/ServoBindings.h
layout/style/StyleSheet.cpp
layout/style/test/gtest/StyloParsingBench.cpp
modules/libpref/init/StaticPrefList.h
servo/components/style/gecko/arc_types.rs
servo/components/style/parser.rs
servo/components/style/properties/declaration_block.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/stylesheets/keyframes_rule.rs
servo/components/style/stylesheets/mod.rs
servo/components/style/stylesheets/stylesheet.rs
servo/components/style/use_counters/mod.rs
servo/ports/geckolib/glue.rs
servo/ports/geckolib/stylesheet_loader.rs
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13432,8 +13432,22 @@ nsIDocument::ReportShadowDOMUsage()
       nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::infoFlag,
                                                   NS_LITERAL_CSTRING("DOM"),
                                                   topLevel);
     }
   }
 
   mHasReportedShadowDOMUsage = true;
 }
+
+const StyleUseCounters*
+nsIDocument::GetStyleUseCounters()
+{
+  if (!StaticPrefs::layout_css_use_counters_enabled()) {
+    return nullptr;
+  }
+
+  if (MOZ_UNLIKELY(!mStyleUseCounters)) {
+    mStyleUseCounters = Servo_UseCounters_Create().Consume();
+  }
+
+  return mStyleUseCounters;
+}
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3397,16 +3397,18 @@ public:
 
   void SetDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
     if (!mUseCounters[aUseCounter]) {
       mUseCounters[aUseCounter] = true;
     }
   }
 
+  const StyleUseCounters* GetStyleUseCounters();
+
   void SetPageUseCounter(mozilla::UseCounter aUseCounter);
 
   void SetDocumentAndPageUseCounter(mozilla::UseCounter aUseCounter)
   {
     SetDocumentUseCounter(aUseCounter);
     SetPageUseCounter(aUseCounter);
   }
 
@@ -4314,16 +4316,19 @@ protected:
   // Flags for use counters used directly by this document.
   std::bitset<mozilla::eUseCounter_Count> mUseCounters;
   // Flags for use counters used by any child documents of this document.
   std::bitset<mozilla::eUseCounter_Count> mChildDocumentUseCounters;
   // Flags for whether we've notified our top-level "page" of a use counter
   // for this child document.
   std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
 
+  // The CSS property use counters.
+  RefPtr<const StyleUseCounters> mStyleUseCounters;
+
   // Whether the user has interacted with the document or not:
   bool mUserHasInteracted;
 
   // Whether the user has interacted with the document via a restricted
   // set of gestures which are likely to be interaction with the document,
   // and not events that are fired as a byproduct of the user interacting
   // with the browser (events for like scrolling the page, keyboard short
   // cuts, etc).
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 /* a list of all Servo Arc types used in stylo bindings for preprocessing */
 
+SERVO_ARC_TYPE(UseCounters, StyleUseCounters)
 SERVO_ARC_TYPE(CssRules, ServoCssRules)
 SERVO_ARC_TYPE(StyleSheetContents, RawServoStyleSheetContents)
 SERVO_ARC_TYPE(DeclarationBlock, RawServoDeclarationBlock)
 SERVO_ARC_TYPE(StyleRule, RawServoStyleRule)
 SERVO_ARC_TYPE(ImportRule, RawServoImportRule)
 SERVO_ARC_TYPE(AnimationValue, RawServoAnimationValue)
 SERVO_ARC_TYPE(Keyframe, RawServoKeyframe)
 SERVO_ARC_TYPE(KeyframesRule, RawServoKeyframesRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -56,25 +56,27 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_From
                    mozilla::css::Loader* loader,
                    mozilla::StyleSheet* gecko_stylesheet,
                    mozilla::css::SheetLoadData* load_data,
                    const nsACString* bytes,
                    mozilla::css::SheetParsingMode parsing_mode,
                    RawGeckoURLExtraData* extra_data,
                    uint32_t line_number_offset,
                    nsCompatibility quirks_mode,
-                   mozilla::css::LoaderReusableStyleSheets* reusable_sheets)
+                   mozilla::css::LoaderReusableStyleSheets* reusable_sheets,
+                   StyleUseCountersBorrowedOrNull use_counters)
 SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8BytesAsync,
                    void,
                    mozilla::css::SheetLoadDataHolder* load_data,
                    RawGeckoURLExtraData* extra_data,
                    const nsACString* bytes,
                    mozilla::css::SheetParsingMode parsing_mode,
                    uint32_t line_number_offset,
-                   nsCompatibility quirks_mode)
+                   nsCompatibility quirks_mode,
+                   StyleUseCountersBorrowedOrNull use_counters)
 SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetContentsStrong,
                    mozilla::css::SheetParsingMode parsing_mode)
 SERVO_BINDING_FUNC(Servo_StyleSheet_HasRules, bool,
                    RawServoStyleSheetContentsBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_GetRules, ServoCssRulesStrong,
                    RawServoStyleSheetContentsBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSheet_Clone, RawServoStyleSheetContentsStrong,
                    RawServoStyleSheetContentsBorrowed sheet,
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -750,11 +750,14 @@ float Gecko_MediaFeatures_GetResolution(
 bool Gecko_MediaFeatures_PrefersReducedMotion(nsIDocument*);
 float Gecko_MediaFeatures_GetDevicePixelRatio(nsIDocument*);
 bool Gecko_MediaFeatures_HasSystemMetric(nsIDocument*,
                                          nsAtom* metric,
                                          bool is_accessible_from_content);
 bool Gecko_MediaFeatures_IsResourceDocument(nsIDocument*);
 nsAtom* Gecko_MediaFeatures_GetOperatingSystemVersion(nsIDocument*);
 
+// Use counters.
+StyleUseCountersStrong Servo_UseCounters_Create();
+
 } // extern "C"
 
 #endif // mozilla_ServoBindings_h
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -1000,48 +1000,54 @@ AllowParallelParse(css::Loader* aLoader,
     return false;
   }
 
   return true;
 }
 
 RefPtr<StyleSheetParsePromise>
 StyleSheet::ParseSheet(css::Loader* aLoader,
-                            const nsACString& aBytes,
-                            css::SheetLoadData* aLoadData)
+                       const nsACString& aBytes,
+                       css::SheetLoadData* aLoadData)
 {
   MOZ_ASSERT(aLoader);
   MOZ_ASSERT(aLoadData);
   MOZ_ASSERT(mParsePromise.IsEmpty());
   RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
   Inner().mURLData =
     new URLExtraData(GetBaseURI(), GetSheetURI(), Principal()); // RefPtr
 
+  const StyleUseCounters* useCounters = aLoader->GetDocument()
+    ? aLoader->GetDocument()->GetStyleUseCounters()
+    : nullptr;
+
   if (!AllowParallelParse(aLoader, GetSheetURI())) {
     RefPtr<RawServoStyleSheetContents> contents =
       Servo_StyleSheet_FromUTF8Bytes(aLoader,
                                      this,
                                      aLoadData,
                                      &aBytes,
                                      mParsingMode,
                                      Inner().mURLData,
                                      aLoadData->mLineNumber,
                                      aLoader->GetCompatibilityMode(),
-                                     /* reusable_sheets = */ nullptr)
+                                     /* reusable_sheets = */ nullptr,
+                                     useCounters)
       .Consume();
     FinishAsyncParse(contents.forget());
   } else {
     RefPtr<css::SheetLoadDataHolder> loadDataHolder =
       new css::SheetLoadDataHolder(__func__, aLoadData);
     Servo_StyleSheet_FromUTF8BytesAsync(loadDataHolder,
                                         Inner().mURLData,
                                         &aBytes,
                                         mParsingMode,
                                         aLoadData->mLineNumber,
-                                        aLoader->GetCompatibilityMode());
+                                        aLoader->GetCompatibilityMode(),
+                                        useCounters);
   }
 
   return p;
 }
 
 void
 StyleSheet::FinishAsyncParse(already_AddRefed<RawServoStyleSheetContents> aSheetContents)
 {
@@ -1058,26 +1064,31 @@ StyleSheet::ParseSheetSync(css::Loader* 
                            const nsACString& aBytes,
                            css::SheetLoadData* aLoadData,
                            uint32_t aLineNumber,
                            css::LoaderReusableStyleSheets* aReusableSheets)
 {
   nsCompatibility compatMode =
     aLoader ? aLoader->GetCompatibilityMode() : eCompatibility_FullStandards;
 
+  const StyleUseCounters* useCounters = aLoader && aLoader->GetDocument()
+    ? aLoader->GetDocument()->GetStyleUseCounters()
+    : nullptr;
+
   Inner().mURLData = new URLExtraData(GetBaseURI(), GetSheetURI(), Principal()); // RefPtr
   Inner().mContents = Servo_StyleSheet_FromUTF8Bytes(aLoader,
                                                      this,
                                                      aLoadData,
                                                      &aBytes,
                                                      mParsingMode,
                                                      Inner().mURLData,
                                                      aLineNumber,
                                                      compatMode,
-                                                     aReusableSheets)
+                                                     aReusableSheets,
+                                                     useCounters)
                          .Consume();
 
   FinishParse();
 }
 
 void
 StyleSheet::FinishParse()
 {
--- a/layout/style/test/gtest/StyloParsingBench.cpp
+++ b/layout/style/test/gtest/StyloParsingBench.cpp
@@ -19,18 +19,18 @@ using namespace mozilla::net;
 
 // Bug 1436018 - Disable Stylo microbenchmark on Windows
 #if !defined(_WIN32) && !defined(_WIN64)
 
 #define PARSING_REPETITIONS 20
 #define SETPROPERTY_REPETITIONS (1000 * 1000)
 #define GETPROPERTY_REPETITIONS (1000 * 1000)
 
-static void ServoParsingBench() {
-
+static void ServoParsingBench()
+{
   auto css = AsBytes(MakeStringSpan(EXAMPLE_STYLESHEET));
   nsCString cssStr;
   cssStr.Append(css);
   ASSERT_EQ(Encoding::UTF8ValidUpTo(css), css.Length());
 
   RefPtr<URLExtraData> data = new URLExtraData(
     NullPrincipalURI::Create(), nullptr, NullPrincipal::CreateWithoutOriginAttributes());
   for (int i = 0; i < PARSING_REPETITIONS; i++) {
@@ -38,22 +38,24 @@ static void ServoParsingBench() {
       Servo_StyleSheet_FromUTF8Bytes(nullptr,
                                      nullptr,
                                      nullptr,
                                      &cssStr,
                                      eAuthorSheetFeatures,
                                      data,
                                      0,
                                      eCompatibility_FullStandards,
+                                     nullptr,
                                      nullptr)
         .Consume();
   }
 }
 
-static void ServoSetPropertyByIdBench(const nsACString& css) {
+static void ServoSetPropertyByIdBench(const nsACString& css)
+{
   RefPtr<RawServoDeclarationBlock> block = Servo_DeclarationBlock_CreateEmpty().Consume();
   RefPtr<URLExtraData> data = new URLExtraData(
     NullPrincipalURI::Create(), nullptr, NullPrincipal::CreateWithoutOriginAttributes());
 
   ASSERT_TRUE(IsUTF8(css));
 
   for (int i = 0; i < SETPROPERTY_REPETITIONS; i++) {
     Servo_DeclarationBlock_SetPropertyById(
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -256,16 +256,29 @@ VARCACHE_PREF(
 
 // Is parallel CSS parsing enabled?
 VARCACHE_PREF(
   "layout.css.parsing.parallel",
    layout_css_parsing_parallel,
   bool, true
 )
 
+// Are style system use counters enabled?
+#ifdef RELEASE_OR_BETA
+#define PREF_VALUE false
+#else
+#define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "layout.css.use-counters.enabled",
+   layout_css_use_counters_enabled,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 // Is CSS error reporting enabled?
 VARCACHE_PREF(
   "layout.css.report_errors",
   layout_css_report_errors,
   bool, true
 )
 
 // Is support for the font-display @font-face descriptor enabled?
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -16,16 +16,17 @@ use gecko_bindings::bindings::RawServoKe
 use gecko_bindings::bindings::RawServoMediaRule;
 use gecko_bindings::bindings::RawServoMozDocumentRule;
 use gecko_bindings::bindings::RawServoNamespaceRule;
 use gecko_bindings::bindings::RawServoPageRule;
 use gecko_bindings::bindings::RawServoRuleNode;
 use gecko_bindings::bindings::RawServoRuleNodeStrong;
 use gecko_bindings::bindings::RawServoSupportsRule;
 use gecko_bindings::bindings::ServoCssRules;
+use gecko_bindings::bindings::StyleUseCounters;
 use gecko_bindings::structs::RawServoAnimationValue;
 use gecko_bindings::structs::RawServoDeclarationBlock;
 use gecko_bindings::structs::RawServoFontFaceRule;
 use gecko_bindings::structs::RawServoMediaList;
 use gecko_bindings::structs::RawServoStyleRule;
 use gecko_bindings::structs::RawServoStyleSheetContents;
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
 use media_queries::MediaList;
@@ -34,16 +35,17 @@ use properties::animated_properties::Ani
 use rule_tree::StrongRuleNode;
 use servo_arc::{Arc, ArcBorrow};
 use shared_lock::Locked;
 use std::{mem, ptr};
 use stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRule};
 use stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule, NamespaceRule, PageRule};
 use stylesheets::{StyleRule, StylesheetContents, SupportsRule};
 use stylesheets::keyframes_rule::Keyframe;
+use use_counters::UseCounters;
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty[$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
 
@@ -54,16 +56,19 @@ macro_rules! impl_arc_ffi {
 
         #[no_mangle]
         pub unsafe extern "C" fn $release(obj: &$gecko_type) {
             <$servo_type>::release(obj);
         }
     };
 }
 
+impl_arc_ffi!(UseCounters => StyleUseCounters
+              [Servo_UseCounters_AddRef, Servo_UseCounters_Release]);
+
 impl_arc_ffi!(Locked<CssRules> => ServoCssRules
               [Servo_CssRules_AddRef, Servo_CssRules_Release]);
 
 impl_arc_ffi!(StylesheetContents => RawServoStyleSheetContents
               [Servo_StyleSheetContents_AddRef, Servo_StyleSheetContents_Release]);
 
 impl_arc_ffi!(Locked<PropertyDeclarationBlock> => RawServoDeclarationBlock
               [Servo_DeclarationBlock_AddRef, Servo_DeclarationBlock_Release]);
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -63,45 +63,48 @@ impl<'a> ParserContext<'a> {
     #[inline]
     pub fn new(
         stylesheet_origin: Origin,
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
         error_reporter: Option<&'a ParseErrorReporter>,
+        use_counters: Option<&'a UseCounters>,
     ) -> Self {
         Self {
             stylesheet_origin,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
             error_reporter,
             namespaces: None,
-            use_counters: None,
+            use_counters,
         }
     }
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     #[inline]
     pub fn new_for_cssom(
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
         error_reporter: Option<&'a ParseErrorReporter>,
+        use_counters: Option<&'a UseCounters>,
     ) -> Self {
         Self::new(
             Origin::Author,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
             error_reporter,
+            use_counters,
         )
     }
 
     /// Create a parser context based on a previous context, but with a modified
     /// rule type.
     #[inline]
     pub fn new_with_rule_type(
         context: &'a ParserContext,
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -1198,16 +1198,17 @@ pub fn parse_style_attribute(
 ) -> PropertyDeclarationBlock {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         quirks_mode,
         error_reporter,
+        None,
     );
 
     let mut input = ParserInput::new(input);
     parse_property_declaration_list(&context, &mut Parser::new(&mut input))
 }
 
 /// Parse a given property declaration. Can result in multiple
 /// `PropertyDeclaration`s when expanding a shorthand, for example.
@@ -1225,16 +1226,17 @@ pub fn parse_one_declaration_into(
 ) -> Result<(), ()> {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         parsing_mode,
         quirks_mode,
         error_reporter,
+        None,
     );
 
     let mut input = ParserInput::new(input);
     let mut parser = Parser::new(&mut input);
     let start_position = parser.position();
     parser.parse_entirely(|parser| {
         PropertyDeclaration::parse_into(declarations, id, &context, parser)
     }).map_err(|err| {
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1558,16 +1558,17 @@ impl UnparsedValue {
             // FIXME(emilio): ParsingMode is slightly fishy...
             let context = ParserContext::new(
                 Origin::Author,
                 &self.url_data,
                 None,
                 ParsingMode::DEFAULT,
                 quirks_mode,
                 None,
+                None,
             );
 
             let mut input = ParserInput::new(&css);
             Parser::new(&mut input).parse_entirely(|input| {
                 match self.from_shorthand {
                     None => longhand_id.parse_value(&context, input),
                     Some(ShorthandId::All) => {
                         // No need to parse the 'all' shorthand as anything other than a CSS-wide
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -214,16 +214,17 @@ impl Keyframe {
         let namespaces = parent_stylesheet_contents.namespaces.read();
         let mut context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             Some(CssRuleType::Keyframe),
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
             None,
+            None,
         );
         context.namespaces = Some(&*namespaces);
         let mut input = ParserInput::new(css);
         let mut input = Parser::new(&mut input);
 
         let mut declarations = SourcePropertyDeclaration::new();
         let mut rule_parser = KeyframeListParser {
             context: &context,
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -259,16 +259,17 @@ impl CssRule {
         let url_data = parent_stylesheet_contents.url_data.read();
         let context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             None,
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
             None,
+            None,
         );
 
         let mut input = ParserInput::new(css);
         let mut input = Parser::new(&mut input);
 
         let mut guard = parent_stylesheet_contents.namespaces.write();
 
         // nested rules are in the body state
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -19,16 +19,17 @@ use shared_lock::{DeepCloneParams, DeepC
 use std::mem;
 use std::sync::atomic::{AtomicBool, Ordering};
 use style_traits::ParsingMode;
 use stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
 use stylesheets::loader::StylesheetLoader;
 use stylesheets::rule_parser::{State, TopLevelRuleParser};
 use stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator};
 use stylesheets::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
+use use_counters::UseCounters;
 
 /// This structure holds the user-agent and user stylesheets.
 pub struct UserAgentStylesheets {
     /// The lock used for user-agent stylesheets.
     pub shared_lock: SharedRwLock,
     /// The user or user agent stylesheets.
     pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>,
     /// The quirks mode stylesheet.
@@ -73,28 +74,30 @@ impl StylesheetContents {
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         shared_lock: &SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
         error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
+        use_counters: Option<&UseCounters>,
     ) -> Self {
         let namespaces = RwLock::new(Namespaces::default());
         let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
             css,
             &url_data,
             origin,
             &mut *namespaces.write(),
             &shared_lock,
             stylesheet_loader,
             error_reporter,
             quirks_mode,
             line_number_offset,
+            use_counters,
         );
 
         Self {
             rules: CssRules::new(rules, &shared_lock),
             origin: origin,
             url_data: RwLock::new(url_data),
             namespaces: namespaces,
             quirks_mode: quirks_mode,
@@ -310,26 +313,29 @@ impl Stylesheet {
         existing: &Stylesheet,
         css: &str,
         url_data: UrlExtraData,
         stylesheet_loader: Option<&StylesheetLoader>,
         error_reporter: Option<&ParseErrorReporter>,
         line_number_offset: u32,
     ) {
         let namespaces = RwLock::new(Namespaces::default());
+
+        // FIXME: Consider adding use counters to Servo?
         let (rules, source_map_url, source_url) = Self::parse_rules(
             css,
             &url_data,
             existing.contents.origin,
             &mut *namespaces.write(),
             &existing.shared_lock,
             stylesheet_loader,
             error_reporter,
             existing.contents.quirks_mode,
             line_number_offset,
+            /* use_counters = */ None,
         );
 
         *existing.contents.url_data.write() = url_data;
         mem::swap(
             &mut *existing.contents.namespaces.write(),
             &mut *namespaces.write(),
         );
 
@@ -345,28 +351,30 @@ impl Stylesheet {
         url_data: &UrlExtraData,
         origin: Origin,
         namespaces: &mut Namespaces,
         shared_lock: &SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
         error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
+        use_counters: Option<&UseCounters>,
     ) -> (Vec<CssRule>, Option<String>, Option<String>) {
         let mut rules = Vec::new();
         let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
         let mut input = Parser::new(&mut input);
 
         let context = ParserContext::new(
             origin,
             url_data,
             None,
             ParsingMode::DEFAULT,
             quirks_mode,
             error_reporter,
+            use_counters,
         );
 
         let rule_parser = TopLevelRuleParser {
             stylesheet_origin: origin,
             shared_lock,
             loader: stylesheet_loader,
             context,
             state: State::Start,
@@ -416,25 +424,27 @@ impl Stylesheet {
         origin: Origin,
         media: Arc<Locked<MediaList>>,
         shared_lock: SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
         error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> Self {
+        // FIXME: Consider adding use counters to Servo?
         let contents = StylesheetContents::from_str(
             css,
             url_data,
             origin,
             &shared_lock,
             stylesheet_loader,
             error_reporter,
             quirks_mode,
             line_number_offset,
+            /* use_counters = */ None,
         );
 
         Stylesheet {
             contents,
             shared_lock,
             media,
             disabled: AtomicBool::new(false),
         }
--- a/servo/components/style/use_counters/mod.rs
+++ b/servo/components/style/use_counters/mod.rs
@@ -28,13 +28,14 @@ impl NonCustomPropertyUseCounters {
         let bit = id.bit();
         let bucket = bit / BITS_PER_ENTRY;
         let bit_in_bucket = bit % BITS_PER_ENTRY;
         self.storage[bucket].fetch_or(1 << bit_in_bucket, Ordering::Relaxed);
     }
 }
 
 /// 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,
 }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -149,16 +149,17 @@ use style::stylesheets::import_rule::Imp
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
 use style::traversal::resolve_style;
 use style::traversal_flags::{self, TraversalFlags};
+use style::use_counters::UseCounters;
 use style::values::{CustomIdent, KeyframesName};
 use style::values::animated::{Animate, Procedure, ToAnimatedZero};
 use style::values::computed::{Context, ToComputedValue};
 use style::values::distance::ComputeSquaredDistance;
 use style::values::generics::rect::Rect;
 use style::values::specified;
 use style::values::specified::gecko::{IntersectionObserverRootMargin, PixelOrPercentage};
 use style::values::specified::source_size_list::SourceSizeList;
@@ -1190,17 +1191,18 @@ pub extern "C" fn Servo_StyleSheet_Empty
         StylesheetContents::from_str(
             "",
             unsafe { dummy_url_data() }.clone(),
             origin,
             shared_lock,
             /* loader = */ None,
             None,
             QuirksMode::NoQuirks,
-            0
+            0,
+            /* use_counters = */ None,
         )
     ).into_strong()
 }
 
 /// Note: The load_data corresponds to this sheet, and is passed as the parent
 /// load data for child sheet loads. It may be null for certain cases where we
 /// know we won't have child loads.
 #[no_mangle]
@@ -1209,16 +1211,17 @@ pub extern "C" fn Servo_StyleSheet_FromU
     stylesheet: *mut DomStyleSheet,
     load_data: *mut SheetLoadData,
     bytes: *const nsACString,
     mode: SheetParsingMode,
     extra_data: *mut URLExtraData,
     line_number_offset: u32,
     quirks_mode: nsCompatibility,
     reusable_sheets: *mut LoaderReusableStyleSheets,
+    use_counters: bindings::StyleUseCountersBorrowedOrNull,
 ) -> RawServoStyleSheetContentsStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let input: &str = unsafe { (*bytes).as_str_unchecked() };
 
     let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
     let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
     let loader = if loader.is_null() {
         None
@@ -1227,50 +1230,58 @@ pub extern "C" fn Servo_StyleSheet_FromU
     };
 
     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
     let loader: Option<&StyleStylesheetLoader> = match loader {
         None => None,
         Some(ref s) => Some(s),
     };
 
-
+    let use_counters = UseCounters::arc_from_borrowed(&use_counters);
     Arc::new(StylesheetContents::from_str(
         input,
         url_data.clone(),
         mode_to_origin(mode),
         &global_style_data.shared_lock,
         loader,
         reporter.as_ref().map(|r| r as &ParseErrorReporter),
         quirks_mode.into(),
         line_number_offset,
+        use_counters.map(|counters| &**counters),
     )).into_strong()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
     load_data: *mut SheetLoadDataHolder,
     extra_data: *mut URLExtraData,
     bytes: *const nsACString,
     mode: SheetParsingMode,
     line_number_offset: u32,
     quirks_mode: nsCompatibility,
+    use_counters: bindings::StyleUseCountersBorrowedOrNull,
 ) {
     let load_data = RefPtr::new(load_data);
     let extra_data = UrlExtraData(RefPtr::new(extra_data));
 
     let mut sheet_bytes = nsCString::new();
     sheet_bytes.assign(&*bytes);
+
+    let use_counters = UseCounters::arc_from_borrowed(&use_counters)
+        .cloned()
+        .map(Arc::from_raw_offset);
+
     let async_parser = AsyncStylesheetParser::new(
         load_data,
         extra_data,
         sheet_bytes,
         mode_to_origin(mode),
         quirks_mode.into(),
-        line_number_offset
+        line_number_offset,
+        use_counters,
     );
 
     if let Some(thread_pool) = STYLE_THREAD_POOL.style_thread_pool.as_ref() {
         thread_pool.spawn(|| {
             async_parser.parse();
         });
     } else {
         async_parser.parse();
@@ -2554,16 +2565,17 @@ pub unsafe extern "C" fn Servo_FontFaceR
     let url_data = UrlExtraData::from_ptr_ref(&data);
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
 
     write_locked_arc(rule, |rule: &mut FontFaceRule| {
         macro_rules! to_css_text {
             (
                 valid: [$($v_enum_name:ident => $field:ident,)*]
                 invalid: [$($i_enum_name:ident,)*]
             ) => {
@@ -2764,16 +2776,17 @@ macro_rules! counter_style_descriptors {
             let url_data = dummy_url_data();
             let context = ParserContext::new(
                 Origin::Author,
                 url_data,
                 Some(CssRuleType::CounterStyle),
                 ParsingMode::DEFAULT,
                 QuirksMode::NoQuirks,
                 None,
+                None,
             );
 
             write_locked_arc(rule, |rule: &mut CounterStyleRule| {
                 match desc {
                     $(nsCSSCounterDesc::$desc => {
                         match parser.parse_entirely(|i| Parse::parse(&context, i)) {
                             Ok(value) => rule.$setter(value),
                             Err(_) => false,
@@ -3324,16 +3337,17 @@ pub extern "C" fn Servo_ParseEasing(
     let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
     let easing = unsafe { (*easing).to_string() };
     let mut input = ParserInput::new(&easing);
     let mut parser = Parser::new(&mut input);
     let result =
         parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
     match result {
         Ok(parsed_easing) => {
@@ -3400,26 +3414,26 @@ pub extern "C" fn Servo_MatrixTransform_
     } else if progress < 0.5 {
         *output = from.clone().into();
     } else {
         *output = to.clone().into();
     }
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_ParseStyleAttribute(
+pub unsafe extern "C" fn Servo_ParseStyleAttribute(
     data: *const nsACString,
     raw_extra_data: *mut URLExtraData,
     quirks_mode: nsCompatibility,
     loader: *mut Loader,
 ) -> RawServoDeclarationBlockStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
-    let value = unsafe { data.as_ref().unwrap().as_str_unchecked() };
+    let value = (*data).as_str_unchecked();
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
-    let url_data = unsafe { UrlExtraData::from_ptr_ref(&raw_extra_data) };
+    let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
     Arc::new(global_style_data.shared_lock.wrap(
         parse_style_attribute(
             value,
             url_data,
             reporter.as_ref().map(|r| r as &ParseErrorReporter),
             quirks_mode.into(),
         )
     )).into_strong()
@@ -3808,16 +3822,17 @@ pub unsafe extern "C" fn Servo_MediaList
     let context = ParserContext::new(
         origin,
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         // TODO(emilio): Looks like error reporting could be useful here?
         None,
+        None,
     );
 
     write_locked_arc(list, |list: &mut MediaList| {
         *list = MediaList::parse(&context, &mut parser);
     })
 }
 
 #[no_mangle]
@@ -3849,16 +3864,17 @@ pub extern "C" fn Servo_MediaList_Append
     let new_medium = unsafe { new_medium.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new_for_cssom(
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
     write_locked_arc(list, |list: &mut MediaList| {
         list.append_medium(&context, new_medium);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_DeleteMedium(
@@ -3868,16 +3884,17 @@ pub extern "C" fn Servo_MediaList_Delete
     let old_medium = unsafe { old_medium.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new_for_cssom(
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
     write_locked_arc(list, |list: &mut MediaList| list.delete_medium(&context, old_medium))
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_SizeOfIncludingThis(
     malloc_size_of: GeckoMallocSizeOf,
     malloc_enclosing_size_of: GeckoMallocSizeOf,
@@ -4310,16 +4327,17 @@ pub extern "C" fn Servo_DeclarationBlock
     let string = unsafe { (*value).to_string() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
     let url = SpecifiedImageUrl::parse_from_string(string.into(), &context);
     let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(
         vec![Either::Second(Image::Url(url))]
     ));
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(decl, Importance::Normal);
     });
@@ -4363,23 +4381,24 @@ pub unsafe extern "C" fn Servo_CSSSuppor
 pub extern "C" fn Servo_CSSSupports(cond: *const nsACString) -> bool {
     let condition = unsafe { cond.as_ref().unwrap().as_str_unchecked() };
     let mut input = ParserInput::new(&condition);
     let mut input = Parser::new(&mut input);
     let cond = input.parse_entirely(|i| parse_condition_or_declaration(i));
     if let Ok(cond) = cond {
         let url_data = unsafe { dummy_url_data() };
         // NOTE(emilio): The supports API is not associated to any stylesheet,
-        // so the fact that there are no namespace map here is fine.
+        // so the fact that there is no namespace map here is fine.
         let context = ParserContext::new_for_cssom(
             url_data,
             Some(CssRuleType::Style),
             ParsingMode::DEFAULT,
             QuirksMode::NoQuirks,
             None,
+            None,
         );
 
         cond.eval(&context)
     } else {
         false
     }
 }
 
@@ -5459,16 +5478,17 @@ fn parse_color(
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         error_reporter,
+        None,
     );
 
     let start_position = parser.position();
     parser.parse_entirely(|i| specified::Color::parse(&context, i)).map_err(|err| {
         if error_reporter.is_some() {
             match err.kind {
                 ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) => {
                     let location = err.location.clone();
@@ -5563,16 +5583,17 @@ pub unsafe extern "C" fn Servo_Intersect
     let url_data = dummy_url_data();
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
 
     let margin = parser.parse_entirely(|p| {
         IntersectionObserverRootMargin::parse(&context, p)
     });
     match margin {
         Ok(margin) => {
             margin.0.to_gecko_rect(result);
@@ -5607,16 +5628,17 @@ pub extern "C" fn Servo_ParseTransformIn
     let mut parser = Parser::new(&mut input);
     let context = ParserContext::new(
         Origin::Author,
         unsafe { dummy_url_data() },
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
 
     let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
         Ok(t) => t,
         Err(..) => return false,
     };
 
     let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
@@ -5650,16 +5672,17 @@ pub extern "C" fn Servo_ParseFontShortha
     let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
 
     let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
         Ok(f) => f,
         Err(..) => return false,
     };
 
     // The system font is not acceptable, so we return false.
@@ -5708,16 +5731,17 @@ pub unsafe extern "C" fn Servo_SourceSiz
 
     let context = ParserContext::new(
         Origin::Author,
         dummy_url_data(),
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
+        None,
     );
 
     // NB: Intentionally not calling parse_entirely.
     let list = SourceSizeList::parse(&context, &mut parser);
     Box::into_raw(Box::new(list)) as *mut _
 }
 
 #[no_mangle]
@@ -5791,8 +5815,13 @@ pub unsafe extern "C" fn Servo_PseudoCla
     let name = name.as_ref().unwrap().as_str_unchecked();
     match NonTSPseudoClass::parse_non_functional(name) {
         None => 0,
         // Ignore :any-link since it contains both visited and unvisited state.
         Some(NonTSPseudoClass::AnyLink) => 0,
         Some(pseudo_class) => pseudo_class.state_flag().bits(),
     }
 }
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_UseCounters_Create() -> bindings::StyleUseCountersStrong {
+    Arc::new(UseCounters::default()).into_strong()
+}
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -15,16 +15,17 @@ use style::gecko_bindings::structs::{Sty
 use style::gecko_bindings::sugar::ownership::FFIArcHelpers;
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::media_queries::MediaList;
 use style::parser::ParserContext;
 use style::shared_lock::{Locked, SharedRwLock};
 use style::stylesheets::{ImportRule, Origin, StylesheetLoader as StyleStylesheetLoader};
 use style::stylesheets::{StylesheetContents, UrlExtraData};
 use style::stylesheets::import_rule::ImportSheet;
+use style::use_counters::UseCounters;
 use style::values::CssUrl;
 
 pub struct StylesheetLoader(*mut Loader, *mut DomStyleSheet, *mut SheetLoadData, *mut LoaderReusableStyleSheets);
 
 impl StylesheetLoader {
     pub fn new(
         loader: *mut Loader,
         parent: *mut DomStyleSheet,
@@ -68,53 +69,56 @@ impl StyleStylesheetLoader for Styleshee
 
 pub struct AsyncStylesheetParser {
     load_data: RefPtr<SheetLoadDataHolder>,
     extra_data: UrlExtraData,
     bytes: nsCString,
     origin: Origin,
     quirks_mode: QuirksMode,
     line_number_offset: u32,
+    use_counters: Option<Arc<UseCounters>>,
 }
 
 impl AsyncStylesheetParser {
     pub fn new(
         load_data: RefPtr<SheetLoadDataHolder>,
         extra_data: UrlExtraData,
         bytes: nsCString,
         origin: Origin,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
+        use_counters: Option<Arc<UseCounters>>,
     ) -> Self {
         AsyncStylesheetParser {
             load_data,
             extra_data,
             bytes,
             origin,
             quirks_mode,
             line_number_offset,
+            use_counters,
         }
     }
 
     pub fn parse(self) {
         let global_style_data = &*GLOBAL_STYLE_DATA;
         let input: &str = unsafe { (*self.bytes).as_str_unchecked() };
 
-        // Note: Parallel CSS parsing doesn't report CSS errors. When errors
-        // are being logged, Gecko prevents the parallel parsing path from
-        // running.
+        // Note: Parallel CSS parsing doesn't report CSS errors. When errors are
+        // being logged, Gecko prevents the parallel parsing path from running.
         let sheet = Arc::new(StylesheetContents::from_str(
             input,
             self.extra_data.clone(),
             self.origin,
             &global_style_data.shared_lock,
             Some(&self),
             None,
             self.quirks_mode.into(),
             self.line_number_offset,
+            self.use_counters.as_ref().map(|counters| &**counters),
         ));
 
         unsafe {
             bindings::Gecko_StyleSheet_FinishAsyncParse(self.load_data.get(), sheet.into_strong());
         }
     }
 }