Bug 1334036 - Part 5: Implement FFI for finding properties overriding animations. draft
authorBoris Chiou <boris.chiou@gmail.com>
Fri, 19 May 2017 15:58:47 +0800
changeset 581938 524bb0f8b5cab4886585a46a4a80093a46614785
parent 581937 bc3f69d0c266dcf0daa5d764e7811ff868fe2cb6
child 581939 aedafdd94b80e36ea1299e5d9b137d61a23f743b
push id59916
push userbmo:boris.chiou@gmail.com
push dateSat, 20 May 2017 06:48:29 +0000
bugs1334036
milestone55.0a1
Bug 1334036 - Part 5: Implement FFI for finding properties overriding animations. We need to traverse rule tree to get the important rules, so we will not override them if they have animations running on compositor. MozReview-Commit-ID: 67NO2nIcUfq
dom/animation/EffectCompositor.cpp
dom/animation/EffectCompositor.h
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
layout/style/ServoBindingList.h
layout/style/ServoBindingTypes.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/AnimationTarget.h"
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EffectSet.h"
 #include "mozilla/LayerAnimationInfo.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
+#include "mozilla/ServoBindings.h" // Servo_GetProperties_Overriding_Animation
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TypeTraits.h" // For Forward<>
 #include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
 #include "nsContentUtils.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h"
@@ -168,17 +169,22 @@ FindAnimationsForCompositor(const nsIFra
   // from test control after seeking where it might not be the case.
   //
   // Those cases are probably not important but just to be safe, let's make
   // sure the cascade is up to date since if it *is* up to date, this is
   // basically a no-op.
   Maybe<NonOwningAnimationTarget> pseudoElement =
     EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
   if (pseudoElement) {
-    EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->mElement,
+    StyleBackendType backend =
+      aFrame->StyleContext()->StyleSource().IsServoComputedValues()
+      ? StyleBackendType::Servo
+      : StyleBackendType::Gecko;
+    EffectCompositor::MaybeUpdateCascadeResults(backend,
+                                                pseudoElement->mElement,
                                                 pseudoElement->mPseudoType,
                                                 aFrame->StyleContext());
   }
 
   if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
     if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
       nsCString message;
       message.AppendLiteral("Performance warning: Async animations are "
@@ -381,17 +387,19 @@ EffectCompositor::UpdateEffectProperties
 void
 EffectCompositor::MaybeUpdateAnimationRule(dom::Element* aElement,
                                            CSSPseudoElementType aPseudoType,
                                            CascadeLevel aCascadeLevel,
                                            nsStyleContext* aStyleContext)
 {
   // First update cascade results since that may cause some elements to
   // be marked as needing a restyle.
-  MaybeUpdateCascadeResults(aElement, aPseudoType, aStyleContext);
+  MaybeUpdateCascadeResults(StyleBackendType::Gecko,
+                            aElement, aPseudoType,
+                            aStyleContext);
 
   auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
   PseudoElementHashEntry::KeyType key = { aElement, aPseudoType };
 
   if (!elementsToRestyle.Contains(key)) {
     return;
   }
 
@@ -582,17 +590,18 @@ EffectCompositor::AddStyleUpdatesTo(Rest
       // Skip animations on elements that have been orphaned since they
       // requested a restyle.
       if (iter.Key().mElement->IsInComposedDoc()) {
         elementsToRestyle.AppendElement(iter.Key());
       }
     }
 
     for (auto& pseudoElem : elementsToRestyle) {
-      MaybeUpdateCascadeResults(pseudoElem.mElement,
+      MaybeUpdateCascadeResults(StyleBackendType::Gecko,
+                                pseudoElem.mElement,
                                 pseudoElem.mPseudoType,
                                 nullptr);
 
       ComposeAnimationRule(pseudoElem.mElement,
                            pseudoElem.mPseudoType,
                            cascadeLevel);
 
       dom::Element* elementToRestyle =
@@ -643,52 +652,44 @@ EffectCompositor::ClearIsRunningOnCompos
   }
 
   for (KeyframeEffectReadOnly* effect : *effects) {
     effect->SetIsRunningOnCompositor(aProperty, false);
   }
 }
 
 /* static */ void
-EffectCompositor::MaybeUpdateCascadeResults(Element* aElement,
+EffectCompositor::MaybeUpdateCascadeResults(StyleBackendType aBackendType,
+                                            Element* aElement,
                                             CSSPseudoElementType aPseudoType,
                                             nsStyleContext* aStyleContext)
 {
   EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
   if (!effects || !effects->CascadeNeedsUpdate()) {
     return;
   }
 
-  nsStyleContext* styleContext = aStyleContext;
-  if (!styleContext) {
-    dom::Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
-    if (elementToRestyle) {
-      nsIFrame* frame = elementToRestyle->GetPrimaryFrame();
-      if (frame) {
-        styleContext = frame->StyleContext();
-      }
-    }
-  }
-  UpdateCascadeResults(*effects, aElement, aPseudoType, styleContext);
+  UpdateCascadeResults(aBackendType, *effects, aElement, aPseudoType,
+                       aStyleContext);
 
   MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
 }
 
 /* static */ void
 EffectCompositor::MaybeUpdateCascadeResults(dom::Element* aElement,
                                             CSSPseudoElementType aPseudoType)
 {
   EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
   MOZ_ASSERT(effects);
   if (!effects->CascadeNeedsUpdate()) {
     return;
   }
 
-  // FIXME: Implement the rule node traversal for stylo in Bug 1334036.
-  UpdateCascadeResults(*effects, aElement, aPseudoType, nullptr);
+  UpdateCascadeResults(StyleBackendType::Servo, *effects, aElement, aPseudoType,
+                       nullptr);
 
   MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
 }
 
 /* static */ Maybe<NonOwningAnimationTarget>
 EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
 {
   // Always return the same object to benefit from return-value optimization.
@@ -760,22 +761,45 @@ EffectCompositor::ComposeAnimationRule(d
     effect->GetAnimation()->WillComposeStyle();
     effect->GetAnimation()->ComposeStyle(animRule, propertiesToSkip);
   }
 
   MOZ_ASSERT(effects == EffectSet::GetEffectSet(aElement, aPseudoType),
              "EffectSet should not change while composing style");
 }
 
-/* static */ void
-EffectCompositor::GetOverriddenProperties(nsStyleContext* aStyleContext,
+/* static */ nsCSSPropertyIDSet
+EffectCompositor::GetOverriddenProperties(StyleBackendType aBackendType,
                                           EffectSet& aEffectSet,
-                                          nsCSSPropertyIDSet&
-                                            aPropertiesOverridden)
+                                          Element* aElement,
+                                          CSSPseudoElementType aPseudoType,
+                                          nsStyleContext* aStyleContext)
 {
+  MOZ_ASSERT(aBackendType != StyleBackendType::Servo || aElement,
+             "Should have an element to get style data from if we are using"
+             " the Servo backend");
+
+  nsCSSPropertyIDSet result;
+
+  Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
+  if (aBackendType == StyleBackendType::Gecko && !aStyleContext) {
+    if (elementToRestyle) {
+      nsIFrame* frame = elementToRestyle->GetPrimaryFrame();
+      if (frame) {
+        aStyleContext = frame->StyleContext();
+      }
+    }
+
+    if (!aStyleContext) {
+      return result;
+    }
+  } else if (aBackendType == StyleBackendType::Servo && !elementToRestyle) {
+    return result;
+  }
+
   AutoTArray<nsCSSPropertyID, LayerAnimationInfo::kRecords> propertiesToTrack;
   {
     nsCSSPropertyIDSet propertiesToTrackAsSet;
     for (KeyframeEffectReadOnly* effect : aEffectSet) {
       for (const AnimationProperty& property : effect->Properties()) {
         if (nsCSSProps::PropHasFlags(property.mProperty,
                                      CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
             !propertiesToTrackAsSet.HasProperty(property.mProperty)) {
@@ -787,26 +811,41 @@ EffectCompositor::GetOverriddenPropertie
       // found all the compositor-animatable properties.
       if (propertiesToTrack.Length() == LayerAnimationInfo::kRecords) {
         break;
       }
     }
   }
 
   if (propertiesToTrack.IsEmpty()) {
-    return;
+    return result;
   }
 
-  nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
-                                                   aStyleContext,
-                                                   aPropertiesOverridden);
+  switch (aBackendType) {
+    case StyleBackendType::Servo:
+      Servo_GetProperties_Overriding_Animation(elementToRestyle,
+                                               &propertiesToTrack,
+                                               &result);
+      break;
+    case StyleBackendType::Gecko:
+      nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
+                                                       aStyleContext,
+                                                       result);
+      break;
+
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unsupported style backend");
+  }
+
+  return result;
 }
 
 /* static */ void
-EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
+EffectCompositor::UpdateCascadeResults(StyleBackendType aBackendType,
+                                       EffectSet& aEffectSet,
                                        Element* aElement,
                                        CSSPseudoElementType aPseudoType,
                                        nsStyleContext* aStyleContext)
 {
   MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet,
              "Effect set should correspond to the specified (pseudo-)element");
   if (aEffectSet.IsEmpty()) {
     aEffectSet.MarkCascadeUpdated();
@@ -820,24 +859,21 @@ EffectCompositor::UpdateCascadeResults(E
   }
   sortedEffectList.Sort(EffectCompositeOrderComparator());
 
   // Get properties that override the *animations* level of the cascade.
   //
   // We only do this for properties that we can animate on the compositor
   // since we will apply other properties on the main thread where the usual
   // cascade applies.
-  nsCSSPropertyIDSet overriddenProperties;
-  if (aStyleContext) {
-    // FIXME: Bug 1334036 (OMTA) will implement a FFI to get the properties
-    // overriding animation.
-    MOZ_ASSERT(!aStyleContext->StyleSource().IsServoComputedValues(),
-               "stylo: Not support get properties overriding animation yet.");
-    GetOverriddenProperties(aStyleContext, aEffectSet, overriddenProperties);
-  }
+  nsCSSPropertyIDSet overriddenProperties =
+    GetOverriddenProperties(aBackendType,
+                            aEffectSet,
+                            aElement, aPseudoType,
+                            aStyleContext);
 
   // Returns a bitset the represents which properties from
   // LayerAnimationInfo::sRecords are present in |aPropertySet|.
   auto compositorPropertiesInSet =
     [](nsCSSPropertyIDSet& aPropertySet) ->
       std::bitset<LayerAnimationInfo::kRecords> {
         std::bitset<LayerAnimationInfo::kRecords> result;
         for (size_t i = 0; i < LayerAnimationInfo::kRecords; i++) {
--- a/dom/animation/EffectCompositor.h
+++ b/dom/animation/EffectCompositor.h
@@ -187,23 +187,29 @@ public:
 
   static void ClearIsRunningOnCompositor(const nsIFrame* aFrame,
                                          nsCSSPropertyID aProperty);
 
   // Update animation cascade results for the specified (pseudo-)element
   // but only if we have marked the cascade as needing an update due a
   // the change in the set of effects or a change in one of the effects'
   // "in effect" state.
-  // |aStyleContext| may be nullptr in which case we will use the
-  // nsStyleContext of the primary frame of the specified (pseudo-)element.
+  //
+  // When |aBackendType| is StyleBackendType::Gecko, |aStyleContext| is used to
+  // find overridden properties. If it is nullptr, the nsStyleContext of the
+  // primary frame of the specified (pseudo-)element, if available, is used.
+  //
+  // When |aBackendType| is StyleBackendType::Servo, we fetch the rule node
+  // from the |aElement| (i.e. |aStyleContext| is ignored).
   //
   // This method does NOT detect if other styles that apply above the
   // animation level of the cascade have changed.
   static void
-  MaybeUpdateCascadeResults(dom::Element* aElement,
+  MaybeUpdateCascadeResults(StyleBackendType aBackendType,
+                            dom::Element* aElement,
                             CSSPseudoElementType aPseudoType,
                             nsStyleContext* aStyleContext);
 
   // Variant of MaybeUpdateCascadeResults for the Servo backend.
   // The Servo backend doesn't use an nsStyleContext to get the rule node
   // to traverse the style tree to find !important rules and instead
   // gets the rule node from |aElement|.
   static void
@@ -252,30 +258,46 @@ private:
                                    CascadeLevel aCascadeLevel);
 
   static dom::Element* GetElementToRestyle(dom::Element* aElement,
                                            CSSPseudoElementType
                                              aPseudoType);
 
   // Get the properties in |aEffectSet| that we are able to animate on the
   // compositor but which are also specified at a higher level in the cascade
-  // than the animations level in |aStyleContext|.
-  static void
-  GetOverriddenProperties(nsStyleContext* aStyleContext,
+  // than the animations level.
+  //
+  // When |aBackendType| is StyleBackendType::Gecko, we determine which
+  // properties are specified using the provided |aStyleContext| and
+  // |aElement| and |aPseudoType| are ignored. If |aStyleContext| is nullptr,
+  // we automatically look up the style context of primary frame of the
+  // (pseudo-)element.
+  //
+  // When |aBackendType| is StyleBackendType::Servo, we use the |StrongRuleNode|
+  // stored on the (pseudo-)element indicated by |aElement| and |aPseudoType|.
+  static nsCSSPropertyIDSet
+  GetOverriddenProperties(StyleBackendType aBackendType,
                           EffectSet& aEffectSet,
-                          nsCSSPropertyIDSet& aPropertiesOverridden);
+                          dom::Element* aElement,
+                          CSSPseudoElementType aPseudoType,
+                          nsStyleContext* aStyleContext);
 
   // Update the mPropertiesWithImportantRules and
   // mPropertiesForAnimationsLevel members of the given EffectSet.
   //
   // This can be expensive so we should only call it if styles that apply
   // above the animation level of the cascade might have changed. For all
   // other cases we should call MaybeUpdateCascadeResults.
+  //
+  // As with MaybeUpdateCascadeResults, |aStyleContext| is only used
+  // when |aBackendType| is StyleBackendType::Gecko. When |aBackendType| is
+  // StyleBackendType::Servo, it is ignored.
   static void
-  UpdateCascadeResults(EffectSet& aEffectSet,
+  UpdateCascadeResults(StyleBackendType aBackendType,
+                       EffectSet& aEffectSet,
                        dom::Element* aElement,
                        CSSPseudoElementType aPseudoType,
                        nsStyleContext* aStyleContext);
 
   static nsPresContext* GetPresContext(dom::Element* aElement);
 
   nsPresContext* mPresContext;
 
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -203,16 +203,17 @@ function treatAsSafeArgument(entry, varN
         ["Gecko_AppendWillChange", "aDisplay", null],
         ["Gecko_CopyWillChangeFrom", "aDest", null],
         ["Gecko_InitializeImageCropRect", "aImage", null],
         ["Gecko_CopyShapeSourceFrom", "aDst", null],
         ["Gecko_DestroyShapeSource", "aShape", null],
         ["Gecko_StyleShapeSource_SetURLValue", "aShape", null],
         ["Gecko_nsFont_InitSystem", "aDest", null],
         ["Gecko_StyleTransition_SetUnsupportedProperty", "aTransition", null],
+        ["Gecko_AddPropertyToSet", "aPropertySet", "null"],
     ];
     for (var [entryMatch, varMatch, csuMatch] of whitelist) {
         assert(entryMatch || varMatch || csuMatch);
         if (entryMatch && !nameMatches(entry.name, entryMatch))
             continue;
         if (varMatch && !nameMatches(varName, varMatch))
             continue;
         if (csuMatch && (!csuName || !nameMatches(csuName, csuMatch)))
@@ -386,16 +387,17 @@ function ignoreContents(entry)
 
         // Needs main thread assertions or other fixes.
         /UndisplayedMap::GetEntryFor/,
         /nsStyleContext::CalcStyleDifferenceInternal/,
         /EffectCompositor::GetServoAnimationRule/,
         /LookAndFeel::GetColor/,
         "Gecko_CopyStyleContentsFrom",
         "Gecko_CSSValue_SetAbsoluteLength",
+        /nsCSSPropertyIDSet::AddProperty/,
     ];
     if (entry.matches(whitelist))
         return true;
 
     if (entry.isSafeArgument(0)) {
         var heapWhitelist = [
             // Operations on heap structures pointed to by arrays and strings are
             // threadsafe as long as the array/string itself is threadsafe.
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -209,16 +209,20 @@ SERVO_BINDING_FUNC(Servo_AnimationValueM
 SERVO_BINDING_FUNC(Servo_ComputedValues_ExtractAnimationValue,
                    RawServoAnimationValueStrong,
                    ServoComputedValuesBorrowed computed_values,
                    nsCSSPropertyID property)
 SERVO_BINDING_FUNC(Servo_Property_IsAnimatable, bool,
                    nsCSSPropertyID property)
 SERVO_BINDING_FUNC(Servo_Property_IsDiscreteAnimatable, bool,
                    nsCSSPropertyID property)
+SERVO_BINDING_FUNC(Servo_GetProperties_Overriding_Animation, void,
+                   RawGeckoElementBorrowed,
+                   RawGeckoCSSPropertyIDListBorrowed,
+                   nsCSSPropertyIDSetBorrowedMut)
 
 // AnimationValues handling
 SERVO_BINDING_FUNC(Servo_AnimationValues_Interpolate,
                    RawServoAnimationValueStrong,
                    RawServoAnimationValueBorrowed from,
                    RawServoAnimationValueBorrowed to,
                    double progress)
 SERVO_BINDING_FUNC(Servo_AnimationValues_IsInterpolable, bool,
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ServoBindingTypes_h
 #define mozilla_ServoBindingTypes_h
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/ServoTypes.h"
 #include "mozilla/UniquePtr.h"
+#include "nsCSSPropertyID.h"
 #include "nsStyleAutoArray.h"
 #include "nsTArray.h"
 
 struct RawServoStyleSet;
 struct RawServoAnimationValueMap;
 
 #define SERVO_ARC_TYPE(name_, type_) struct type_;
 #include "mozilla/ServoArcTypeList.h"
@@ -30,16 +31,17 @@ class StyleChildrenIterator;
 } // namespace dom
 struct AnimationPropertySegment;
 struct ComputedTiming;
 struct Keyframe;
 struct PropertyStyleAnimationValuePair;
 using ComputedKeyframeValues = nsTArray<PropertyStyleAnimationValuePair>;
 } // namespace mozilla
 
+class nsCSSPropertyIDSet;
 class nsCSSValue;
 struct nsFontFaceRuleContainer;
 class nsIDocument;
 class nsINode;
 class nsPresContext;
 struct nsTimingFunction;
 
 using mozilla::dom::StyleChildrenIterator;
@@ -53,16 +55,17 @@ typedef mozilla::URLExtraData RawGeckoUR
 typedef nsTArray<mozilla::Keyframe> RawGeckoKeyframeList;
 typedef nsTArray<mozilla::ComputedKeyframeValues> RawGeckoComputedKeyframeValuesList;
 typedef nsTArray<mozilla::PropertyStyleAnimationValuePair> RawGeckoAnimationValueList;
 typedef nsStyleAutoArray<mozilla::StyleAnimation> RawGeckoStyleAnimationList;
 typedef nsTArray<nsFontFaceRuleContainer> RawGeckoFontFaceRuleList;
 typedef mozilla::AnimationPropertySegment RawGeckoAnimationPropertySegment;
 typedef mozilla::ComputedTiming RawGeckoComputedTiming;
 typedef nsTArray<const RawServoStyleRule*> RawGeckoServoStyleRuleList;
+typedef nsTArray<nsCSSPropertyID> RawGeckoCSSPropertyIDList;
 
 // We have these helper types so that we can directly generate
 // things like &T or Borrowed<T> on the Rust side in the function, providing
 // additional safety benefits.
 //
 // FFI has a problem with templated types, so we just use raw pointers here.
 //
 // The "Borrowed" types generate &T or Borrowed<T> in the nullable case.
@@ -134,16 +137,18 @@ DECL_BORROWED_REF_TYPE_FOR(RawGeckoKeyfr
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoComputedKeyframeValuesList)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoStyleAnimationList)
 DECL_BORROWED_MUT_REF_TYPE_FOR(nsTimingFunction)
 DECL_BORROWED_REF_TYPE_FOR(nsTimingFunction)
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoFontFaceRuleList)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoAnimationPropertySegment)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoComputedTiming)
 DECL_BORROWED_MUT_REF_TYPE_FOR(RawGeckoServoStyleRuleList)
+DECL_BORROWED_MUT_REF_TYPE_FOR(nsCSSPropertyIDSet)
+DECL_BORROWED_REF_TYPE_FOR(RawGeckoCSSPropertyIDList)
 
 #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
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2107,16 +2107,23 @@ Gecko_CSSFontFaceRule_GetCssText(const n
   // hazard when dealing with color values (nsCSSKeywords::AddRefTable)
   // We only serialize on the main thread; assert to convince the analysis
   // and prevent accidentally calling this elsewhere
   MOZ_ASSERT(NS_IsMainThread());
 
   aRule->GetCssText(*aResult);
 }
 
+void
+Gecko_AddPropertyToSet(nsCSSPropertyIDSetBorrowedMut aPropertySet,
+                       nsCSSPropertyID aProperty)
+{
+  aPropertySet->AddProperty(aProperty);
+}
+
 NS_IMPL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule);
 
 nsCSSCounterStyleRule*
 Gecko_CSSCounterStyle_Create(nsIAtom* aName)
 {
   RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(aName, 0, 0);
   return rule.forget().take();
 }
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -531,16 +531,18 @@ RawGeckoElementBorrowedOrNull Gecko_GetB
 nscolor Gecko_GetLookAndFeelSystemColor(int32_t color_id,
                                         RawGeckoPresContextBorrowed pres_context);
 
 bool Gecko_MatchStringArgPseudo(RawGeckoElementBorrowed element,
                                 mozilla::CSSPseudoClassType type,
                                 const char16_t* ident,
                                 bool* set_slow_selector);
 
+void Gecko_AddPropertyToSet(nsCSSPropertyIDSetBorrowedMut, nsCSSPropertyID);
+
 // Style-struct management.
 #define STYLE_STRUCT(name, checkdata_cb)                                       \
   void Gecko_Construct_Default_nsStyle##name(                                  \
     nsStyle##name* ptr,                                                        \
     RawGeckoPresContextBorrowed pres_context);                                 \
   void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr,                   \
                                          const nsStyle##name* other);          \
   void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -154,16 +154,17 @@ whitelist-types = [
     "nsAttrValue",
     "nsBorderColors",
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterStyleRule",
     "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
+    "nsCSSPropertyIDSet",
     "nsCSSProps",
     "nsCSSRect",
     "nsCSSRect_heap",
     "nsCSSShadowArray",
     "nsCSSValue",
     "nsCSSValueFloatColor",
     "nsCSSValueGradient",
     "nsCSSValueGradientStop",
@@ -310,16 +311,17 @@ raw-lines = [
 whitelist-functions = ["Servo_.*", "Gecko_.*"]
 structs-types = [
     "mozilla::css::GridTemplateAreasValue",
     "mozilla::css::ImageValue",
     "mozilla::css::URLValue",
     "mozilla::Side",
     "RawGeckoAnimationPropertySegment",
     "RawGeckoComputedTiming",
+    "RawGeckoCSSPropertyIDList",
     "RawGeckoDocument",
     "RawGeckoElement",
     "RawGeckoKeyframeList",
     "RawGeckoComputedKeyframeValuesList",
     "RawGeckoFontFaceRuleList",
     "RawGeckoNode",
     "RawGeckoAnimationValueList",
     "RawServoAnimationValue",
@@ -348,16 +350,17 @@ structs-types = [
     "StyleBasicShape",
     "StyleBasicShapeType",
     "StyleShapeSource",
     "StyleTransition",
     "nsCSSCounterStyleRule",
     "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
+    "nsCSSPropertyIDSet",
     "nsCSSShadowArray",
     "nsCSSUnit",
     "nsCSSValue",
     "nsCSSValueSharedList",
     "nsChangeHint",
     "nsCursorImage",
     "nsFont",
     "nsIAtom",
@@ -432,21 +435,23 @@ servo-immutable-borrow-types = [
     "RawGeckoNode",
     "RawGeckoElement",
     "RawGeckoDocument",
     "RawServoDeclarationBlockStrong",
     "RawGeckoPresContext",
     "RawGeckoStyleAnimationList",
 ]
 servo-borrow-types = [
+    "nsCSSPropertyIDSet",
     "nsCSSValue",
     "nsTimingFunction",
     "RawGeckoAnimationPropertySegment",
     "RawGeckoAnimationValueList",
     "RawGeckoComputedTiming",
+    "RawGeckoCSSPropertyIDList",
     "RawGeckoKeyframeList",
     "RawGeckoComputedKeyframeValuesList",
     "RawGeckoFontFaceRuleList",
     "RawGeckoServoStyleRuleList",
 ]
 fixups = [
     # hack for gecko-owned string
     { pat = "<nsString", rep = "<nsStringRepr" },