Bug 1280772 - Part 3: Store Servo-parsed style="" attributes in nsAttrValues. r=bholley
authorCameron McCormack <cam@mcc.id.au>
Fri, 24 Jun 2016 13:35:12 +1000
changeset 302514 ddd91b2e949dc1e244da2f10602f06e522e6a5a0
parent 302513 e64637e1c4b2f9b9badcff70cd57c41c7f4de8bd
child 302515 d49eededc392f7d8c5bb8a639649abb60b0aae68
push id30363
push usercbook@mozilla.com
push dateFri, 24 Jun 2016 09:14:27 +0000
treeherdermozilla-central@939ecc4e9d05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs1280772
milestone50.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 1280772 - Part 3: Store Servo-parsed style="" attributes in nsAttrValues. r=bholley
dom/base/nsAttrValue.cpp
dom/base/nsAttrValue.h
dom/base/nsAttrValueInlines.h
dom/base/nsStyledElement.cpp
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/nsHTMLCSSStyleSheet.cpp
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/HashFunctions.h"
 
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsIAtom.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/css/Declaration.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "prprf.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsCSSParser.h"
 #include "nsStyledElement.h"
 #include "nsIURI.h"
@@ -67,54 +68,89 @@ MiscContainer::GetString(nsAString& aStr
   atom->ToString(aString);
   return true;
 }
 
 void
 MiscContainer::Cache()
 {
   // Not implemented for anything else yet.
-  MOZ_ASSERT(mType == nsAttrValue::eGeckoCSSDeclaration);
+  MOZ_ASSERT(mType == nsAttrValue::eGeckoCSSDeclaration ||
+             mType == nsAttrValue::eServoCSSDeclaration);
   MOZ_ASSERT(IsRefCounted());
   MOZ_ASSERT(mValue.mRefCount > 0);
   MOZ_ASSERT(!mValue.mCached);
 
-  css::Declaration* declaration = mValue.mGeckoCSSDeclaration;
-  nsHTMLCSSStyleSheet* sheet = declaration->GetHTMLCSSStyleSheet();
+  nsHTMLCSSStyleSheet* sheet;
+  switch (mType) {
+    case nsAttrValue::eGeckoCSSDeclaration:
+      sheet = mValue.mGeckoCSSDeclaration->GetHTMLCSSStyleSheet();
+      break;
+    case nsAttrValue::eServoCSSDeclaration:
+      sheet = Servo_GetDeclarationBlockCache(mValue.mServoCSSDeclaration);
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
+      sheet = nullptr;
+      break;
+  }
+
   if (!sheet) {
     return;
   }
 
   nsString str;
   bool gotString = GetString(str);
   if (!gotString) {
     return;
   }
 
   sheet->CacheStyleAttr(str, this);
   mValue.mCached = 1;
 
   // This has to be immutable once it goes into the cache.
-  declaration->SetImmutable();
+  switch (mType) {
+    case nsAttrValue::eGeckoCSSDeclaration:
+      mValue.mGeckoCSSDeclaration->SetImmutable();
+      break;
+    case nsAttrValue::eServoCSSDeclaration:
+      Servo_SetDeclarationBlockImmutable(mValue.mServoCSSDeclaration);
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
+      break;
+  }
 }
 
 void
 MiscContainer::Evict()
 {
   // Not implemented for anything else yet.
-  MOZ_ASSERT(mType == nsAttrValue::eGeckoCSSDeclaration);
+  MOZ_ASSERT(mType == nsAttrValue::eGeckoCSSDeclaration ||
+             mType == nsAttrValue::eServoCSSDeclaration);
   MOZ_ASSERT(IsRefCounted());
   MOZ_ASSERT(mValue.mRefCount == 0);
 
   if (!mValue.mCached) {
     return;
   }
 
-  css::Declaration* declaration = mValue.mGeckoCSSDeclaration;
-  nsHTMLCSSStyleSheet* sheet = declaration->GetHTMLCSSStyleSheet();
+  nsHTMLCSSStyleSheet* sheet;
+  switch (mType) {
+    case nsAttrValue::eGeckoCSSDeclaration:
+      sheet = mValue.mGeckoCSSDeclaration->GetHTMLCSSStyleSheet();
+      break;
+    case nsAttrValue::eServoCSSDeclaration:
+      sheet = Servo_GetDeclarationBlockCache(mValue.mServoCSSDeclaration);
+      break;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
+      sheet = nullptr;
+      break;
+  }
   MOZ_ASSERT(sheet);
 
   nsString str;
   DebugOnly<bool> gotString = GetString(str);
   MOZ_ASSERT(gotString);
 
   sheet->EvictStyleAttr(str, this);
   mValue.mCached = 0;
@@ -303,16 +339,17 @@ nsAttrValue::SetTo(const nsAttrValue& aO
       break;
     }
     case eColor:
     {
       cont->mValue.mColor = otherCont->mValue.mColor;
       break;
     }
     case eGeckoCSSDeclaration:
+    case eServoCSSDeclaration:
     {
       MOZ_CRASH("These should be refcounted!");
     }
     case eURL:
     {
       NS_ADDREF(cont->mValue.mURL = otherCont->mValue.mURL);
       break;
     }
@@ -421,16 +458,29 @@ nsAttrValue::SetTo(css::Declaration* aVa
   NS_ADDREF(cont->mValue.mGeckoCSSDeclaration = aValue);
   cont->mType = eGeckoCSSDeclaration;
   NS_ADDREF(cont);
   SetMiscAtomOrString(aSerialized);
   MOZ_ASSERT(cont->mValue.mRefCount == 1);
 }
 
 void
+nsAttrValue::SetTo(ServoDeclarationBlock* aValue,
+                   const nsAString* aSerialized)
+{
+  MiscContainer* cont = EnsureEmptyMiscContainer();
+  MOZ_ASSERT(cont->mValue.mRefCount == 0);
+  cont->mValue.mServoCSSDeclaration = aValue;
+  cont->mType = eServoCSSDeclaration;
+  NS_ADDREF(cont);
+  SetMiscAtomOrString(aSerialized);
+  MOZ_ASSERT(cont->mValue.mRefCount == 1);
+}
+
+void
 nsAttrValue::SetTo(css::URLValue* aValue, const nsAString* aSerialized)
 {
   MiscContainer* cont = EnsureEmptyMiscContainer();
   NS_ADDREF(cont->mValue.mURL = aValue);
   cont->mType = eURL;
   SetMiscAtomOrString(aSerialized);
 }
 
@@ -629,16 +679,20 @@ nsAttrValue::ToString(nsAString& aResult
       nsAutoString intStr;
       intStr.AppendInt(cont ? cont->mValue.mPercent : GetIntInternal());
       aResult = intStr + NS_LITERAL_STRING("%");
 
       break;
     }
     case eGeckoCSSDeclaration:
     {
+      // XXXheycam Once we support CSSOM access to them, we should
+      // probably serialize eServoCSSDeclarations like this too.
+      // For now, we will return the string from the MiscContainer
+      // at the top of this function.
       aResult.Truncate();
       MiscContainer *container = GetMiscContainer();
       css::Declaration *decl = container->mValue.mGeckoCSSDeclaration;
       if (decl) {
         decl->ToString(aResult);
       }
       const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
 
@@ -880,16 +934,20 @@ nsAttrValue::HashValue() const
     case eColor:
     {
       return cont->mValue.mColor;
     }
     case eGeckoCSSDeclaration:
     {
       return NS_PTR_TO_INT32(cont->mValue.mGeckoCSSDeclaration);
     }
+    case eServoCSSDeclaration:
+    {
+      return NS_PTR_TO_INT32(cont->mValue.mServoCSSDeclaration);
+    }
     // Intentionally identical, so that loading the image does not change the
     // hash code.
     case eURL:
     case eImage:
     {
       nsString str;
       ToString(str);
       return HashString(str);
@@ -1018,16 +1076,21 @@ nsAttrValue::Equals(const nsAttrValue& a
     case eDoubleValue:
     {
       return thisCont->mDoubleValue == otherCont->mDoubleValue;
     }
     case eIntMarginValue:
     {
       return thisCont->mValue.mIntMargin == otherCont->mValue.mIntMargin;
     }
+    case eServoCSSDeclaration:
+    {
+      return thisCont->mValue.mServoCSSDeclaration ==
+               otherCont->mValue.mServoCSSDeclaration;
+    }
     default:
     {
       if (IsSVGType(thisCont->mType)) {
         // Currently this method is never called for nsAttrValue objects that
         // point to SVG data types.
         // If that changes then we probably want to add methods to the
         // corresponding SVG types to compare their base values.
         // As a shortcut, however, we can begin by comparing the pointers.
@@ -1675,52 +1738,63 @@ nsAttrValue::ParseStyleAttribute(const n
     if (cont) {
       // Set our MiscContainer to the cached one.
       NS_ADDREF(cont);
       SetPtrValueAndType(cont, eOtherBase);
       return true;
     }
   }
 
-  css::Loader* cssLoader = ownerDoc->CSSLoader();
-  nsCSSParser cssParser(cssLoader);
+  if (ownerDoc->GetStyleBackendType() == StyleBackendType::Servo) {
+    NS_ConvertUTF16toUTF8 value(aString);
+    ServoDeclarationBlock* decl = Servo_ParseStyleAttribute(
+        reinterpret_cast<const uint8_t*>(value.get()),
+        value.Length(),
+        sheet);
+    MOZ_ASSERT(decl);
+    SetTo(decl, &aString);
+  } else {
+    css::Loader* cssLoader = ownerDoc->CSSLoader();
+    nsCSSParser cssParser(cssLoader);
 
-  RefPtr<css::Declaration> declaration =
-    cssParser.ParseStyleAttribute(aString, docURI, baseURI,
-                                  aElement->NodePrincipal());
-  if (declaration) {
+    RefPtr<css::Declaration> declaration =
+      cssParser.ParseStyleAttribute(aString, docURI, baseURI,
+                                    aElement->NodePrincipal());
+    if (!declaration) {
+      return false;
+    }
     declaration->SetHTMLCSSStyleSheet(sheet);
     SetTo(declaration, &aString);
-    if (cachingAllowed) {
-      MiscContainer* cont = GetMiscContainer();
-      cont->Cache();
-    }
-
-    return true;
   }
 
-  return false;
+  if (cachingAllowed) {
+    MiscContainer* cont = GetMiscContainer();
+    cont->Cache();
+  }
+
+  return true;
 }
 
 void
 nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
 {
   NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
   NS_ASSERTION(!GetMiscContainer()->mStringBits,
                "Trying to re-set atom or string!");
   if (aValue) {
     uint32_t len = aValue->Length();
-    // * We're allowing eGeckoCSSDeclaration attributes to store empty
-    //   strings as it can be beneficial to store an empty style attribute
-    //   as a parsed rule.
+    // * We're allowing eGeckoCSSDeclaration and eServoCSSDeclaration
+    //   attributes to store empty strings as it can be beneficial to store
+    //   an empty style attribute as a parsed rule.
     // * We're allowing enumerated values because sometimes the empty
     //   string corresponds to a particular enumerated value, especially
     //   for enumerated values that are not limited enumerated.
     // Add other types as needed.
-    NS_ASSERTION(len || Type() == eGeckoCSSDeclaration || Type() == eEnum,
+    NS_ASSERTION(len || Type() == eGeckoCSSDeclaration ||
+                 Type() == eServoCSSDeclaration || Type() == eEnum,
                  "Empty string?");
     MiscContainer* cont = GetMiscContainer();
     if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) {
       nsCOMPtr<nsIAtom> atom = NS_Atomize(*aValue);
       if (atom) {
         cont->mStringBits =
           reinterpret_cast<uintptr_t>(atom.forget().take()) | eAtomBase;
       }
@@ -1775,21 +1849,26 @@ nsAttrValue::ClearMiscContainer()
       NS_RELEASE(cont);
 
       cont = new MiscContainer;
       SetPtrValueAndType(cont, eOtherBase);
     }
     else {
       switch (cont->mType) {
         case eGeckoCSSDeclaration:
+        case eServoCSSDeclaration:
         {
           MOZ_ASSERT(cont->mValue.mRefCount == 1);
           cont->Release();
           cont->Evict();
-          NS_RELEASE(cont->mValue.mGeckoCSSDeclaration);
+          if (cont->mType == eGeckoCSSDeclaration) {
+            NS_RELEASE(cont->mValue.mGeckoCSSDeclaration);
+          } else {
+            Servo_DropDeclarationBlock(cont->mValue.mServoCSSDeclaration);
+          }
           break;
         }
         case eURL:
         {
           NS_RELEASE(cont->mValue.mURL);
           break;
         }
         case eImage:
@@ -1914,16 +1993,21 @@ nsAttrValue::SizeOfExcludingThis(MallocS
         n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0;
       }
 
       if (Type() == eGeckoCSSDeclaration &&
           container->mValue.mGeckoCSSDeclaration) {
         // TODO: mGeckoCSSDeclaration might be owned by another object which
         //       would make us count them twice, bug 677493.
         //n += container->mGeckoCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf);
+      } else if (Type() == eServoCSSDeclaration &&
+                 container->mValue.mServoCSSDeclaration) {
+        // Bug 1281964: As with eGeckoCSSDeclaration, but if we do measure we'll
+        // need a way to call the Servo heap_size_of function for the
+        // declaration block.
       } else if (Type() == eAtomArray && container->mValue.mAtomArray) {
         // Don't measure each nsIAtom, they are measured separatly.
         n += container->mValue.mAtomArray->ShallowSizeOfIncludingThis(aMallocSizeOf);
       }
       break;
     }
     case eAtomBase:    // Atoms are counted separately.
     case eIntegerBase: // The value is in mBits, nothing to do.
--- a/dom/base/nsAttrValue.h
+++ b/dom/base/nsAttrValue.h
@@ -27,16 +27,17 @@
 
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 
 class nsAString;
 class nsIDocument;
 class nsStyledElementNotElementCSSInlineStyle;
 struct MiscContainer;
+struct ServoDeclarationBlock;
 
 namespace mozilla {
 namespace css {
 class Declaration;
 struct URLValue;
 struct ImageValue;
 } // namespace css
 } // namespace mozilla
@@ -90,16 +91,17 @@ public:
     eAtom =         0x02, //   10
     eInteger =      0x03, // 0011
     eColor =        0x07, // 0111
     eEnum =         0x0B, // 1011  This should eventually die
     ePercent =      0x0F, // 1111
     // Values below here won't matter, they'll be always stored in the 'misc'
     // struct.
     eGeckoCSSDeclaration = 0x10,
+    eServoCSSDeclaration,
     eURL,
     eImage,
     eAtomArray,
     eDoubleValue,
     eIntMarginValue,
     eSVGAngle,
     eSVGTypesBegin = eSVGAngle,
     eSVGIntegerPair,
@@ -141,16 +143,18 @@ public:
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
   void SetTo(nsIAtom* aValue);
   void SetTo(int16_t aInt);
   void SetTo(int32_t aInt, const nsAString* aSerialized);
   void SetTo(double aValue, const nsAString* aSerialized);
   void SetTo(mozilla::css::Declaration* aValue, const nsAString* aSerialized);
+  void SetTo(ServoDeclarationBlock* aDeclarationBlock,
+             const nsAString* aSerialized);
   void SetTo(mozilla::css::URLValue* aValue, const nsAString* aSerialized);
   void SetTo(const nsIntMargin& aValue);
   void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized);
   void SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized);
   void SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized);
   void SetTo(const mozilla::SVGLengthList& aValue,
              const nsAString* aSerialized);
   void SetTo(const mozilla::SVGNumberList& aValue,
@@ -192,16 +196,17 @@ public:
   const nsCheapString GetStringValue() const;
   inline nsIAtom* GetAtomValue() const;
   inline int32_t GetIntegerValue() const;
   bool GetColorValue(nscolor& aColor) const;
   inline int16_t GetEnumValue() const;
   inline float GetPercentValue() const;
   inline AtomArray* GetAtomArrayValue() const;
   inline mozilla::css::Declaration* GetGeckoCSSDeclarationValue() const;
+  inline ServoDeclarationBlock* GetServoCSSDeclarationValue() const;
   inline mozilla::css::URLValue* GetURLValue() const;
   inline mozilla::css::ImageValue* GetImageValue() const;
   inline double GetDoubleValue() const;
   bool GetIntMarginValue(nsIntMargin& aMargin) const;
 
   /**
    * Returns the string corresponding to the stored enum value.
    *
--- a/dom/base/nsAttrValueInlines.h
+++ b/dom/base/nsAttrValueInlines.h
@@ -27,16 +27,17 @@ struct MiscContainer final
   union {
     struct {
       union {
         int32_t mInteger;
         nscolor mColor;
         uint32_t mEnumValue;
         int32_t mPercent;
         mozilla::css::Declaration* mGeckoCSSDeclaration;
+        ServoDeclarationBlock* mServoCSSDeclaration;
         mozilla::css::URLValue* mURL;
         mozilla::css::ImageValue* mImage;
         nsAttrValue::AtomArray* mAtomArray;
         nsIntMargin* mIntMargin;
         const nsSVGAngle* mSVGAngle;
         const nsSVGIntegerPair* mSVGIntegerPair;
         const nsSVGLength2* mSVGLength;
         const mozilla::SVGLengthList* mSVGLengthList;
@@ -80,18 +81,19 @@ protected:
 
 public:
   bool GetString(nsAString& aString) const;
 
   inline bool IsRefCounted() const
   {
     // Nothing stops us from refcounting (and sharing) other types of
     // MiscContainer (except eDoubleValue types) but there's no compelling
-    // reason to 
-    return mType == nsAttrValue::eGeckoCSSDeclaration;
+    // reason to.
+    return mType == nsAttrValue::eGeckoCSSDeclaration ||
+           mType == nsAttrValue::eServoCSSDeclaration;
   }
 
   inline int32_t AddRef() {
     MOZ_ASSERT(IsRefCounted());
     return ++mValue.mRefCount;
   }
 
   inline int32_t Release() {
@@ -148,16 +150,23 @@ nsAttrValue::GetAtomArrayValue() const
 
 inline mozilla::css::Declaration*
 nsAttrValue::GetGeckoCSSDeclarationValue() const
 {
   NS_PRECONDITION(Type() == eGeckoCSSDeclaration, "wrong type");
   return GetMiscContainer()->mValue.mGeckoCSSDeclaration;
 }
 
+inline ServoDeclarationBlock*
+nsAttrValue::GetServoCSSDeclarationValue() const
+{
+  NS_PRECONDITION(Type() == eServoCSSDeclaration, "wrong type");
+  return GetMiscContainer()->mValue.mServoCSSDeclaration;
+}
+
 inline mozilla::css::URLValue*
 nsAttrValue::GetURLValue() const
 {
   NS_PRECONDITION(Type() == eURL, "wrong type");
   return GetMiscContainer()->mValue.mURL;
 }
 
 inline mozilla::css::ImageValue*
@@ -193,17 +202,19 @@ nsAttrValue::IsSVGType(ValueType aType) 
 
 inline bool
 nsAttrValue::StoresOwnData() const
 {
   if (BaseType() != eOtherBase) {
     return true;
   }
   ValueType t = Type();
-  return t != eGeckoCSSDeclaration && !IsSVGType(t);
+  return t != eGeckoCSSDeclaration &&
+         t != eServoCSSDeclaration &&
+         !IsSVGType(t);
 }
 
 inline void
 nsAttrValue::SetPtrValueAndType(void* aValue, ValueBaseType aType)
 {
   NS_ASSERTION(!(NS_PTR_TO_INT32(aValue) & ~NS_ATTRVALUE_POINTERVALUE_MASK),
                "pointer not properly aligned, this will crash");
   mBits = reinterpret_cast<intptr_t>(aValue) | aType;
--- a/dom/base/nsStyledElement.cpp
+++ b/dom/base/nsStyledElement.cpp
@@ -126,17 +126,22 @@ nsStyledElementNotElementCSSInlineStyle:
 nsresult
 nsStyledElementNotElementCSSInlineStyle::ReparseStyleAttribute(bool aForceInDataDoc)
 {
   if (!MayHaveStyle()) {
     return NS_OK;
   }
   const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
   
-  if (oldVal && oldVal->Type() != nsAttrValue::eGeckoCSSDeclaration) {
+  nsAttrValue::ValueType desiredType =
+    OwnerDoc()->GetStyleBackendType() == StyleBackendType::Gecko ?
+      nsAttrValue::eGeckoCSSDeclaration :
+      nsAttrValue::eServoCSSDeclaration;
+
+  if (oldVal && oldVal->Type() != desiredType) {
     nsAttrValue attrValue;
     nsAutoString stringValue;
     oldVal->ToString(stringValue);
     ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc);
     // Don't bother going through SetInlineStyleDeclaration; we don't
     // want to fire off mutation events or document notifications anyway
     nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -212,16 +212,26 @@ Gecko_ClassOrClassList(RawGeckoElement* 
   static_assert(alignof(nsCOMPtr<nsIAtom>) == alignof(nsIAtom*), "Bad simplification");
 
   nsCOMPtr<nsIAtom>* elements = atomArray->Elements();
   nsIAtom** rawElements = reinterpret_cast<nsIAtom**>(elements);
   *aClassList = rawElements;
   return atomArray->Length();
 }
 
+ServoDeclarationBlock*
+Gecko_GetServoDeclarationBlock(RawGeckoElement* aElement)
+{
+  const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style);
+  if (!attr || attr->Type() != nsAttrValue::eServoCSSDeclaration) {
+    return nullptr;
+  }
+  return attr->GetServoCSSDeclarationValue();
+}
+
 ServoNodeData*
 Gecko_GetNodeData(RawGeckoNode* aNode)
 {
   return aNode->GetServoNodeData();
 }
 
 void
 Gecko_SetNodeData(RawGeckoNode* aNode, ServoNodeData* aData)
@@ -513,16 +523,52 @@ Servo_InitStyleSet()
 
 void
 Servo_DropStyleSet(RawServoStyleSet* set)
 {
   MOZ_CRASH("stylo: shouldn't be calling Servo_DropStyleSet in a "
             "non-MOZ_STYLO build");
 }
 
+ServoDeclarationBlock*
+Servo_ParseStyleAttribute(const uint8_t* bytes, uint8_t length,
+                          nsHTMLCSSStyleSheet* cache)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_ParseStyleAttribute in a "
+            "non-MOZ_STYLO build");
+}
+
+void
+Servo_DropDeclarationBlock(ServoDeclarationBlock* declarations)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_DropDeclarationBlock in a "
+            "non-MOZ_STYLO build");
+}
+
+nsHTMLCSSStyleSheet*
+Servo_GetDeclarationBlockCache(ServoDeclarationBlock* declarations)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_GetDeclarationBlockCache in a "
+            "non-MOZ_STYLO build");
+}
+
+void
+Servo_SetDeclarationBlockImmutable(ServoDeclarationBlock* declarations)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_SetDeclarationBlockImmutable in a "
+            "non-MOZ_STYLO build");
+}
+
+void
+Servo_ClearDeclarationBlockCachePointer(ServoDeclarationBlock* declarations)
+{
+  MOZ_CRASH("stylo: shouldn't be calling Servo_ClearDeclarationBlockCachePointer in a "
+            "non-MOZ_STYLO build");
+}
+
 ServoComputedValues*
 Servo_GetComputedValues(RawGeckoNode* node)
 {
   MOZ_CRASH("stylo: shouldn't be calling Servo_GetComputedValues in a "
             "non-MOZ_STYLO build");
 }
 
 ServoComputedValues*
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -36,22 +36,24 @@ using mozilla::FontFamilyType;
 using mozilla::dom::Element;
 typedef mozilla::dom::Element RawGeckoElement;
 class nsIDocument;
 typedef nsIDocument RawGeckoDocument;
 struct ServoNodeData;
 struct ServoComputedValues;
 struct RawServoStyleSheet;
 struct RawServoStyleSet;
+class nsHTMLCSSStyleSheet;
 struct nsStyleList;
 struct nsStyleImage;
 struct nsStyleGradientStop;
 class nsStyleGradient;
 class nsStyleCoord;
 struct nsStyleDisplay;
+struct ServoDeclarationBlock;
 
 #define NS_DECL_THREADSAFE_FFI_REFCOUNTING(class_, name_)                     \
   void Gecko_AddRef##name_##ArbitraryThread(class_* aPtr);                    \
   void Gecko_Release##name_##ArbitraryThread(class_* aPtr);
 #define NS_IMPL_THREADSAFE_FFI_REFCOUNTING(class_, name_)                     \
   static_assert(class_::HasThreadSafeRefCnt::value,                           \
                 "NS_DECL_THREADSAFE_FFI_REFCOUNTING can only be used with "   \
                 "classes that have thread-safe refcounting");                 \
@@ -109,16 +111,19 @@ nsIAtom* Gecko_GetElementId(RawGeckoElem
 // If two or more, the classList outparam is set to point to an array of atoms
 // representing the class list.
 //
 // The array is borrowed and the atoms are not addrefed. These values can be
 // invalidated by any DOM mutation. Use them in a tight scope.
 uint32_t Gecko_ClassOrClassList(RawGeckoElement* element,
                                 nsIAtom** class_, nsIAtom*** classList);
 
+// Style attributes.
+ServoDeclarationBlock* Gecko_GetServoDeclarationBlock(RawGeckoElement* element);
+
 // Node data.
 ServoNodeData* Gecko_GetNodeData(RawGeckoNode* node);
 void Gecko_SetNodeData(RawGeckoNode* node, ServoNodeData* data);
 void Servo_DropNodeData(ServoNodeData* data);
 
 // Atoms.
 nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength);
 void Gecko_AddRefAtom(nsIAtom* aAtom);
@@ -178,16 +183,26 @@ void Servo_PrependStyleSheet(RawServoSty
 void Servo_RemoveStyleSheet(RawServoStyleSheet* sheet, RawServoStyleSet* set);
 void Servo_InsertStyleSheetBefore(RawServoStyleSheet* sheet,
                                   RawServoStyleSheet* reference,
                                   RawServoStyleSet* set);
 bool Servo_StyleSheetHasRules(RawServoStyleSheet* sheet);
 RawServoStyleSet* Servo_InitStyleSet();
 void Servo_DropStyleSet(RawServoStyleSet* set);
 
+// Style attributes.
+ServoDeclarationBlock* Servo_ParseStyleAttribute(const uint8_t* bytes,
+                                                 uint8_t length,
+                                                 nsHTMLCSSStyleSheet* cache);
+void Servo_DropDeclarationBlock(ServoDeclarationBlock* declarations);
+nsHTMLCSSStyleSheet* Servo_GetDeclarationBlockCache(
+    ServoDeclarationBlock* declarations);
+void Servo_SetDeclarationBlockImmutable(ServoDeclarationBlock* declarations);
+void Servo_ClearDeclarationBlockCachePointer(ServoDeclarationBlock* declarations);
+
 // Computed style data.
 ServoComputedValues* Servo_GetComputedValues(RawGeckoNode* node);
 ServoComputedValues* Servo_GetComputedValuesForAnonymousBox(ServoComputedValues* parentStyleOrNull,
                                                             nsIAtom* pseudoTag,
                                                             RawServoStyleSet* set);
 ServoComputedValues* Servo_GetComputedValuesForPseudoElement(ServoComputedValues* parent_style,
                                                              RawGeckoElement* match_element,
                                                              nsIAtom* pseudo_tag,
--- a/layout/style/nsHTMLCSSStyleSheet.cpp
+++ b/layout/style/nsHTMLCSSStyleSheet.cpp
@@ -33,22 +33,34 @@ nsHTMLCSSStyleSheet::~nsHTMLCSSStyleShee
 {
   // We may go away before all of our cached style attributes do,
   // so clean up any that are left.
   for (auto iter = mCachedStyleAttrs.Iter(); !iter.Done(); iter.Next()) {
     MiscContainer*& value = iter.Data();
 
     // Ideally we'd just call MiscContainer::Evict, but we can't do that since
     // we're iterating the hashtable.
-    MOZ_ASSERT(value->mType == nsAttrValue::eGeckoCSSDeclaration);
+    switch (value->mType) {
+      case nsAttrValue::eGeckoCSSDeclaration: {
+        css::Declaration* declaration = value->mValue.mGeckoCSSDeclaration;
+        declaration->SetHTMLCSSStyleSheet(nullptr);
+        break;
+      }
+      case nsAttrValue::eServoCSSDeclaration: {
+        ServoDeclarationBlock* declarations =
+          value->mValue.mServoCSSDeclaration;
+        Servo_ClearDeclarationBlockCachePointer(declarations);
+        break;
+      }
+      default:
+        MOZ_ASSERT_UNREACHABLE("unexpected cached nsAttrValue type");
+        break;
+    }
 
-    css::Declaration* declaration = value->mValue.mGeckoCSSDeclaration;
-    declaration->SetHTMLCSSStyleSheet(nullptr);
     value->mValue.mCached = 0;
-
     iter.Remove();
   }
 }
 
 NS_IMPL_ISUPPORTS(nsHTMLCSSStyleSheet, nsIStyleRuleProcessor)
 
 /* virtual */ void
 nsHTMLCSSStyleSheet::RulesMatching(ElementRuleProcessorData* aData)