Bug 1425700 - Make the counters non-atomic counters and merge afterwards. r=bholley
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 21 Aug 2018 12:31:11 +0200
changeset 433596 d5203d9dc33b2f829f621c30e99dbfa5a4bed32c
parent 433595 42822c922dcfd2664b3a5d357a6b8d1f792c5d85
child 433597 20a43066e367897769f5c155b1daba955cfaeea7
push id34518
push useraciure@mozilla.com
push dateTue, 28 Aug 2018 21:58:56 +0000
treeherdermozilla-central@3205543f957c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
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 - Make the counters non-atomic counters and merge afterwards. r=bholley This was consistently faster in the benchmark (even when counters were disabled, which was slightly suspicious, but...). Anyway, it's not really much code, most of it is FFI copy-pasta. Differential Revision: https://phabricator.services.mozilla.com/D3874
dom/base/nsDocument.cpp
dom/base/nsIDocument.h
layout/style/ServoArcTypeList.h
layout/style/ServoBindingList.h
layout/style/ServoBindingTypes.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/StyleSheet.cpp
layout/style/test/gtest/StyloParsingBench.cpp
servo/components/style/gecko/arc_types.rs
servo/components/style/gecko_bindings/sugar/ownership.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
@@ -1515,16 +1515,20 @@ nsIDocument::nsIDocument()
     mServoRestyleRootDirtyBits(0),
     mThrowOnDynamicMarkupInsertionCounter(0),
     mIgnoreOpensDuringUnloadCounter(0),
     mNumTrackersFound(0),
     mNumTrackersBlocked(0)
 {
   SetIsInDocument();
   SetIsConnected(true);
+
+  if (StaticPrefs::layout_css_use_counters_enabled()) {
+    mStyleUseCounters.reset(Servo_UseCounters_Create());
+  }
 }
 
 nsDocument::nsDocument(const char* aContentType)
   : nsIDocument()
 {
   SetContentTypeInternal(nsDependentCString(aContentType));
 
   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
@@ -13432,22 +13436,8 @@ 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,17 +3397,20 @@ public:
 
   void SetDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
     if (!mUseCounters[aUseCounter]) {
       mUseCounters[aUseCounter] = true;
     }
   }
 
-  const StyleUseCounters* GetStyleUseCounters();
+  const StyleUseCounters* GetStyleUseCounters()
+  {
+    return mStyleUseCounters.get();
+  }
 
   void SetPageUseCounter(mozilla::UseCounter aUseCounter);
 
   void SetDocumentAndPageUseCounter(mozilla::UseCounter aUseCounter)
   {
     SetDocumentUseCounter(aUseCounter);
     SetPageUseCounter(aUseCounter);
   }
@@ -4317,17 +4320,17 @@ protected:
   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;
+  mozilla::UniquePtr<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
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -1,17 +1,16 @@
 /* -*- 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
@@ -66,17 +66,17 @@ SERVO_BINDING_FUNC(Servo_StyleSheet_From
 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,
-                   StyleUseCountersBorrowedOrNull use_counters)
+                   bool should_record_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,
@@ -903,16 +903,21 @@ SERVO_BINDING_FUNC(Servo_Property_IsShor
 SERVO_BINDING_FUNC(Servo_Property_IsInherited, bool,
                    const nsACString* name);
 SERVO_BINDING_FUNC(Servo_Property_SupportsType, bool,
                    const nsACString* name, uint32_t ty, bool* found);
 SERVO_BINDING_FUNC(Servo_Property_GetCSSValuesForProperty, void,
                    const nsACString* name, bool* found, nsTArray<nsString>* result)
 SERVO_BINDING_FUNC(Servo_PseudoClass_GetStates, uint64_t,
                    const nsACString* name)
+SERVO_BINDING_FUNC(Servo_UseCounters_Create, StyleUseCounters*)
+SERVO_BINDING_FUNC(Servo_UseCounters_Drop, void, StyleUseCountersOwned)
+SERVO_BINDING_FUNC(Servo_UseCounters_Merge, void,
+                   StyleUseCountersBorrowed doc_counters,
+                   StyleUseCountersBorrowed sheet_counters)
 SERVO_BINDING_FUNC(Servo_IsCssPropertyRecordedInUseCounter, bool,
                    StyleUseCountersBorrowed,
                    const nsACString* property,
                    bool* out_known_prop)
 
 // AddRef / Release functions
 #define SERVO_ARC_TYPE(name_, type_)                                \
   SERVO_BINDING_FUNC(Servo_##name_##_AddRef, void, type_##Borrowed) \
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -18,16 +18,17 @@
 #include "nsTArray.h"
 
 struct RawServoAuthorStyles;
 struct RawServoStyleSet;
 struct RawServoSelectorList;
 struct RawServoSourceSizeList;
 struct RawServoAnimationValueMap;
 struct RustString;
+struct StyleUseCounters;
 
 #define SERVO_ARC_TYPE(name_, type_) struct type_;
 #include "mozilla/ServoArcTypeList.h"
 #undef SERVO_ARC_TYPE
 
 namespace mozilla {
 class ServoElementSnapshot;
 class ComputedStyle;
@@ -194,16 +195,20 @@ DECL_BORROWED_MUT_REF_TYPE_FOR(nsCSSProp
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoCSSPropertyIDList)
 DECL_BORROWED_REF_TYPE_FOR(nsXBLBinding)
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoStyleChildrenIterator)
 DECL_OWNED_REF_TYPE_FOR(RawServoSelectorList)
 DECL_BORROWED_REF_TYPE_FOR(RawServoSelectorList)
 DECL_OWNED_REF_TYPE_FOR(RawServoSourceSizeList)
 DECL_BORROWED_REF_TYPE_FOR(RawServoSourceSizeList)
 DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawServoSourceSizeList)
+DECL_OWNED_REF_TYPE_FOR(StyleUseCounters)
+DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleUseCounters)
+DECL_BORROWED_REF_TYPE_FOR(StyleUseCounters)
+DECL_NULLABLE_BORROWED_REF_TYPE_FOR(StyleUseCounters)
 
 #undef DECL_ARC_REF_TYPE_FOR
 #undef DECL_OWNED_REF_TYPE_FOR
 #undef DECL_NULLABLE_OWNED_REF_TYPE_FOR
 #undef DECL_BORROWED_REF_TYPE_FOR
 #undef DECL_NULLABLE_BORROWED_REF_TYPE_FOR
 #undef DECL_BORROWED_MUT_REF_TYPE_FOR
 #undef DECL_NULLABLE_BORROWED_MUT_REF_TYPE_FOR
@@ -239,16 +244,17 @@ DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawS
     }                                                       \
   };                                                        \
   }
 
 DEFINE_BOXED_TYPE(StyleSet, RawServoStyleSet);
 DEFINE_BOXED_TYPE(AuthorStyles, RawServoAuthorStyles);
 DEFINE_BOXED_TYPE(SelectorList, RawServoSelectorList);
 DEFINE_BOXED_TYPE(SourceSizeList, RawServoSourceSizeList);
+DEFINE_BOXED_TYPE(UseCounters, StyleUseCounters);
 
 #undef DEFINE_BOXED_TYPE
 
 // used for associating sheet type with specific @font-face rules
 struct nsFontFaceRuleContainer
 {
   RefPtr<RawServoFontFaceRule> mRule;
   mozilla::SheetType mSheetType;
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2573,25 +2573,34 @@ Gecko_GetAppUnitsPerPhysicalInch(RawGeck
   nsPresContext* presContext = const_cast<nsPresContext*>(aPresContext);
   return presContext->DeviceContext()->AppUnitsPerPhysicalInch();
 }
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(SheetLoadDataHolder, SheetLoadDataHolder);
 
 void
 Gecko_StyleSheet_FinishAsyncParse(SheetLoadDataHolder* aData,
-                                  RawServoStyleSheetContentsStrong aSheetContents)
+                                  RawServoStyleSheetContentsStrong aSheetContents,
+                                  StyleUseCountersOwned aUseCounters)
 {
+  UniquePtr<StyleUseCounters> useCounters(aUseCounters);
   RefPtr<SheetLoadDataHolder> loadData = aData;
   RefPtr<RawServoStyleSheetContents> sheetContents = aSheetContents.Consume();
   NS_DispatchToMainThread(NS_NewRunnableFunction(__func__,
                                                  [d = std::move(loadData),
-                                                  s = std::move(sheetContents)]() mutable {
+                                                  contents = std::move(sheetContents),
+                                                  counters = std::move(useCounters)]() mutable {
     MOZ_ASSERT(NS_IsMainThread());
-    d->get()->mSheet->FinishAsyncParse(s.forget());
+    SheetLoadData* data = d->get();
+    if (nsIDocument* doc = data->mLoader->GetDocument()) {
+      if (const StyleUseCounters* docCounters = doc->GetStyleUseCounters()) {
+        Servo_UseCounters_Merge(docCounters, counters.get());
+      }
+    }
+    data->mSheet->FinishAsyncParse(contents.forget());
   }));
 }
 
 static already_AddRefed<StyleSheet>
 LoadImportSheet(css::Loader* aLoader,
                 StyleSheet* aParent,
                 SheetLoadData* aParentLoadData,
                 css::LoaderReusableStyleSheets* aReusableSheets,
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -166,17 +166,18 @@ void Gecko_ComputedStyle_Destroy(mozilla
 void Gecko_ConstructStyleChildrenIterator(RawGeckoElementBorrowed aElement,
                                           RawGeckoStyleChildrenIteratorBorrowedMut aIterator);
 void Gecko_DestroyStyleChildrenIterator(RawGeckoStyleChildrenIteratorBorrowedMut aIterator);
 RawGeckoNodeBorrowedOrNull Gecko_GetNextStyleChild(RawGeckoStyleChildrenIteratorBorrowedMut it);
 
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::SheetLoadDataHolder, SheetLoadDataHolder);
 
 void Gecko_StyleSheet_FinishAsyncParse(mozilla::css::SheetLoadDataHolder* data,
-                                       RawServoStyleSheetContentsStrong sheet_contents);
+                                       RawServoStyleSheetContentsStrong sheet_contents,
+                                       StyleUseCountersOwnedOrNull use_counters);
 
 mozilla::StyleSheet*
 Gecko_LoadStyleSheet(mozilla::css::Loader* loader,
                      mozilla::StyleSheet* parent,
                      mozilla::css::SheetLoadData* parent_load_data,
                      mozilla::css::LoaderReusableStyleSheets* reusable_sheets,
                      ServoBundledURI url,
                      RawServoMediaListStrong media_list);
@@ -750,14 +751,11 @@ 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/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -510,16 +510,17 @@ structs-types = [
     "RawGeckoStyleChildrenIterator",
     "RawGeckoServoStyleRuleList",
     "RawGeckoURLExtraData",
     "RawGeckoXBLBinding",
     "RawServoSelectorList",
     "RawServoSourceSizeList",
     "RefPtr",
     "RustString",
+    "StyleUseCounters",
     "CSSPseudoElementType",
     "ServoTraversalFlags",
     "ComputedTimingFunction_BeforeFlag",
     "CounterStylePtr",
     "FontFamilyType",
     "FontSizePrefs",
     "GeckoFontMetrics",
     "IterationCompositeOperation",
@@ -622,16 +623,17 @@ structs-types = [
 array-types = [
     { cpp-type = "uintptr_t", rust-type = "usize" },
 ]
 servo-owned-types = [
     { name = "RawServoStyleSet", opaque = true },
     { name = "RawServoAuthorStyles", opaque = true },
     { name = "RawServoSelectorList", opaque = false },
     { name = "RawServoSourceSizeList", opaque = false },
+    { name = "StyleUseCounters", opaque = false },
     { name = "ServoElementSnapshot", opaque = false },
     { name = "RawServoAnimationValueMap", opaque = true },
 ]
 servo-immutable-borrow-types = [
     "RawGeckoNode",
     "RawGeckoElement",
     "RawGeckoDocument",
     "RawServoDeclarationBlockStrong",
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -1037,17 +1037,17 @@ StyleSheet::ParseSheet(css::Loader* aLoa
     RefPtr<css::SheetLoadDataHolder> loadDataHolder =
       new css::SheetLoadDataHolder(__func__, aLoadData);
     Servo_StyleSheet_FromUTF8BytesAsync(loadDataHolder,
                                         Inner().mURLData,
                                         &aBytes,
                                         mParsingMode,
                                         aLoadData->mLineNumber,
                                         aLoader->GetCompatibilityMode(),
-                                        useCounters);
+                                        /* should_record_counters = */ !!useCounters);
   }
 
   return p;
 }
 
 void
 StyleSheet::FinishAsyncParse(already_AddRefed<RawServoStyleSheetContents> aSheetContents)
 {
--- a/layout/style/test/gtest/StyloParsingBench.cpp
+++ b/layout/style/test/gtest/StyloParsingBench.cpp
@@ -102,18 +102,18 @@ static void ServoGetPropertyValueById() 
   }
 }
 
 MOZ_GTEST_BENCH(Stylo, Servo_StyleSheet_FromUTF8Bytes_Bench, [] {
     ServoParsingBench(nullptr);
 });
 
 MOZ_GTEST_BENCH(Stylo, Servo_StyleSheet_FromUTF8Bytes_Bench_UseCounters, [] {
-    RefPtr<StyleUseCounters> counters = Servo_UseCounters_Create().Consume();
-    ServoParsingBench(counters);
+    UniquePtr<StyleUseCounters> counters(Servo_UseCounters_Create());
+    ServoParsingBench(counters.get());
 });
 
 MOZ_GTEST_BENCH(Stylo, Servo_DeclarationBlock_SetPropertyById_Bench, [] {
   ServoSetPropertyByIdBench(NS_LITERAL_CSTRING("10px"));
 });
 
 MOZ_GTEST_BENCH(Stylo, Servo_DeclarationBlock_SetPropertyById_WithInitialSpace_Bench, [] {
   ServoSetPropertyByIdBench(NS_LITERAL_CSTRING(" 10px"));
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -16,17 +16,16 @@ 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;
@@ -35,17 +34,16 @@ 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 {}
 
@@ -56,19 +54,16 @@ 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/gecko_bindings/sugar/ownership.rs
+++ b/servo/components/style/gecko_bindings/sugar/ownership.rs
@@ -300,16 +300,25 @@ impl<GeckoType> DerefMut for Owned<Gecko
 ///
 /// Can be null, and just as `Owned` leaks on `Drop`.
 pub struct OwnedOrNull<GeckoType> {
     ptr: *mut GeckoType,
     _marker: PhantomData<GeckoType>,
 }
 
 impl<GeckoType> OwnedOrNull<GeckoType> {
+    /// Returns a null pointer.
+    #[inline]
+    pub fn null() -> Self {
+        Self {
+            ptr: ptr::null_mut(),
+            _marker: PhantomData,
+        }
+    }
+
     /// Returns whether this pointer is null.
     #[inline]
     pub fn is_null(&self) -> bool {
         self.ptr.is_null()
     }
 
     /// Returns an owned pointer if this is non-null, and `None` otherwise.
     pub fn into_box_opt<ServoType>(self) -> Option<Box<ServoType>>
--- a/servo/components/style/use_counters/mod.rs
+++ b/servo/components/style/use_counters/mod.rs
@@ -1,29 +1,29 @@
 /* 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/. */
 
 //! Various stuff for CSS property use counters.
 
+#[cfg(feature = "gecko")]
+use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
 use properties::{NonCustomPropertyId, NON_CUSTOM_PROPERTY_ID_COUNT};
-// FIXME(emilio): We need AtomicU32 on stable ASAP...
-use std::sync::atomic::AtomicUsize;
-use std::sync::atomic::Ordering;
+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 NonCustomPropertyUseCounters {
-    storage: [AtomicUsize; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY],
+    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();
@@ -31,27 +31,57 @@ impl NonCustomPropertyUseCounters {
         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);
-        self.storage[bucket].fetch_or(pattern, Ordering::Relaxed);
+        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].load(Ordering::Relaxed) & pattern != 0
+        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())
+        }
     }
 }
 
 /// 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,
 }
+
+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)
+    }
+}
+
+#[cfg(feature = "gecko")]
+unsafe impl HasFFI for UseCounters {
+    type FFIType = ::gecko_bindings::structs::StyleUseCounters;
+}
+
+#[cfg(feature = "gecko")]
+unsafe impl HasSimpleFFI for UseCounters {}
+
+#[cfg(feature = "gecko")]
+unsafe impl HasBoxFFI for UseCounters {}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1230,58 +1230,53 @@ 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),
+        use_counters.map(UseCounters::from_ffi),
     )).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,
+    should_record_use_counters: bool,
 ) {
     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,
-        use_counters,
+        should_record_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();
@@ -5817,27 +5812,39 @@ pub unsafe extern "C" fn Servo_PseudoCla
         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()
+pub unsafe extern "C" fn Servo_UseCounters_Create() -> *mut structs::StyleUseCounters {
+    Box::into_raw(Box::<UseCounters>::default()) as *mut _
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_UseCounters_Drop(c: bindings::StyleUseCountersOwned) {
+    let _ = c.into_box::<UseCounters>();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_UseCounters_Merge(
+    doc_counters: bindings::StyleUseCountersBorrowed,
+    sheet_counters: bindings::StyleUseCountersBorrowed,
+) {
+    UseCounters::from_ffi(doc_counters).merge(UseCounters::from_ffi(sheet_counters))
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_IsCssPropertyRecordedInUseCounter(
     use_counters: bindings::StyleUseCountersBorrowed,
     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,
     };
 
-    let use_counters = UseCounters::as_arc(&use_counters);
-    use_counters.non_custom_properties.recorded(non_custom_id)
-}
+    UseCounters::from_ffi(use_counters).non_custom_properties.recorded(non_custom_id)
+}
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -7,17 +7,17 @@ use nsstring::nsCString;
 use servo_arc::Arc;
 use style::context::QuirksMode;
 use style::gecko::data::GeckoStyleSheet;
 use style::gecko::global_style_data::GLOBAL_STYLE_DATA;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::Gecko_LoadStyleSheet;
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{StyleSheet as DomStyleSheet, SheetLoadData, SheetLoadDataHolder};
-use style::gecko_bindings::sugar::ownership::FFIArcHelpers;
+use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasBoxFFI, OwnedOrNull};
 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;
@@ -69,60 +69,75 @@ 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>>,
+    should_record_use_counters: bool,
 }
 
 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>>,
+        should_record_use_counters: bool,
     ) -> Self {
         AsyncStylesheetParser {
             load_data,
             extra_data,
             bytes,
             origin,
             quirks_mode,
             line_number_offset,
-            use_counters,
+            should_record_use_counters,
         }
     }
 
     pub fn parse(self) {
         let global_style_data = &*GLOBAL_STYLE_DATA;
         let input: &str = unsafe { (*self.bytes).as_str_unchecked() };
 
+        let use_counters = if self.should_record_use_counters {
+            Some(Box::new(UseCounters::default()))
+         } else {
+             None
+         };
+
         // 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),
+            use_counters.as_ref().map(|c| &**c),
         ));
 
+        let use_counters = match use_counters {
+            Some(c) => c.into_ffi().maybe(),
+            None => OwnedOrNull::null(),
+        };
+
         unsafe {
-            bindings::Gecko_StyleSheet_FinishAsyncParse(self.load_data.get(), sheet.into_strong());
+            bindings::Gecko_StyleSheet_FinishAsyncParse(
+                self.load_data.get(),
+                sheet.into_strong(),
+                use_counters,
+            );
         }
     }
 }
 
 impl StyleStylesheetLoader for AsyncStylesheetParser {
     fn request_stylesheet(
         &self,
         url: CssUrl,