Bug 1449087 part 2 - Use Servo data to back @font-face rule. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Tue, 03 Apr 2018 17:01:51 +1000
changeset 776608 a1d41b4c389a6f98d43e471c4a61c9d636b911f1
parent 776600 4985ad63cdfc9ab3c5e2142342b3acce4deb081d
push id104924
push userxquan@mozilla.com
push dateTue, 03 Apr 2018 13:37:23 +0000
reviewersemilio
bugs1449087
milestone61.0a1
Bug 1449087 part 2 - Use Servo data to back @font-face rule. r?emilio This patch does the following things: * Create a new class ServoFontFaceRule for CSSOM of @font-face rule which mostly follows how nsCSSFontFaceRule was implemented. * Remove the old nsCSSFontFaceRule and binding code to create it. * Have FontFace backed by Servo data via making mRule and mDescriptors of the class hold RawServoFontFaceRule like ServoFontFaceRule. To keep this patch small, it effectively just delays the conversion from Servo data to nsCSSValue from parsing to using. This may cause worse performance if the font set is flushed repeatedly. Supposing we don't flush font set very frequently, it may not be a big deal. We may still want to remove the intermediate nsCSSValue conversion at some point, and have everything converted to their final form directly when used, but that can happen in followups. There are some unfortunate bits from this change: * We lose style sheet for logging in FontFaceSet. This is probably not all that worse, because we wouldn't have that before either if the page doesn't use CSSOM to visit it. But we should figure out some approach to fix it anyway. * InspectorFontFace no longer shares the same rule object as CSSOM. This isn't really a problem if the @font-face rule isn't very mutable. Unless we want to make the rule returned from InspectorFontFace to be mutable (i.e. via inspector), not using the same object probably isn't too bad. This patch switches the code we use to serialize stuff in FontFace and CSSFontFaceRule, which leads to some failures in tests. Specifically, the expected changes including: * Value of font-family now can be serialized to identifier sequence like font-family property. The old code always serializes it to string, but it doesn't seem to have different requirement than the property. Blink can serialize to identifier as well. * Family name inside local() is also changed to use the same way as family names elsewhere (i.e. can be identifier sequence). Blink has the same behavior as the old code, but I don't think it's a big deal. * The order of descriptors serialized gets changed. I don't think it matters at all. * Empty string as font-family via using string syntax is no longer considered invalid for FontFace. I don't find it is mentioned anywhere that it should be specifically treated invalid. MozReview-Commit-ID: 32Fk3Fi9uTs
devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
dom/bindings/Bindings.conf
layout/inspector/InspectorFontFace.cpp
layout/inspector/InspectorFontFace.h
layout/style/FontFace.cpp
layout/style/FontFace.h
layout/style/FontFaceSet.cpp
layout/style/FontFaceSet.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/ServoCSSParser.cpp
layout/style/ServoCSSParser.h
layout/style/ServoCSSRuleList.cpp
layout/style/ServoFontFaceRule.cpp
layout/style/ServoFontFaceRule.h
layout/style/moz.build
layout/style/nsCSSFontFaceRule.cpp
layout/style/nsCSSFontFaceRule.h
layout/style/test/mochitest.ini
layout/style/test/test_font_face_parser.html
layout/style/test/test_font_loading_api.html
servo/components/style/font_face.rs
servo/components/style/gecko/arc_types.rs
servo/components/style/gecko/generated/bindings.rs
servo/components/style/gecko/generated/structs.rs
servo/components/style/gecko/rules.rs
servo/components/style/gecko_bindings/sugar/refptr.rs
servo/components/style/stylesheets/font_face_rule.rs
servo/components/style/stylesheets/mod.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
@@ -15,37 +15,37 @@ add_task(async function() {
   info("Checking that the css font-face rule is collapsed by default");
   let fontEl = getUsedFontsEls(viewDoc)[0];
   let codeEl = fontEl.querySelector(".font-css-code");
   is(codeEl.textContent, "@font-face {}", "The font-face rule is collapsed");
 
   info("Expanding the rule by clicking on the expander icon");
   let onExpanded = BrowserTestUtils.waitForCondition(() => {
     return codeEl.textContent === `@font-face {
-  font-family: "bar";
+  font-family: bar;
   src: url("bad/font/name.ttf"), url("ostrich-regular.ttf") format("truetype");
 }`;
-  }, "Waiting for the font-face rule");
+  }, "Waiting for the font-face rule 1");
 
   let expander = fontEl.querySelector(".font-css-code .theme-twisty");
   expander.click();
   await onExpanded;
 
   ok(true, "Font-face rule is now expanded");
 
   info("Expanding another rule by clicking on the [...] icon instead");
   fontEl = getUsedFontsEls(viewDoc)[1];
   codeEl = fontEl.querySelector(".font-css-code");
 
   onExpanded = BrowserTestUtils.waitForCondition(() => {
     return codeEl.textContent === `@font-face {
-  font-family: "bar";
-  font-weight: bold;
+  font-family: bar;
   src: url("ostrich-black.ttf");
+  font-weight: bold;
 }`;
-  }, "Waiting for the font-face rule");
+  }, "Waiting for the font-face rule 2");
 
   expander = fontEl.querySelector(".font-css-code .font-css-code-expander");
   expander.click();
   await onExpanded;
 
   ok(true, "Font-face rule is now expanded too");
 });
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -182,18 +182,18 @@ DOMInterfaces = {
 },
 
 'CSSCounterStyleRule': {
     'nativeType': 'nsCSSCounterStyleRule',
     'headerFile': 'nsCSSCounterStyleRule.h',
 },
 
 'CSSFontFaceRule': {
-    'nativeType': 'nsCSSFontFaceRule',
-    'headerFile': 'nsCSSFontFaceRule.h',
+    'nativeType': 'mozilla::ServoFontFaceRule',
+    'headerFile': 'mozilla/ServoFontFaceRule.h',
 },
 
 'CSSGroupingRule': {
     'concrete': False,
     'nativeType': 'mozilla::css::GroupRule',
 },
 
 'CSSLexer': {
--- a/layout/inspector/InspectorFontFace.cpp
+++ b/layout/inspector/InspectorFontFace.cpp
@@ -8,16 +8,17 @@
 
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "nsFontFaceLoader.h"
 #include "mozilla/gfx/2D.h"
 #include "brotli/decode.h"
 #include "zlib.h"
 #include "mozilla/dom/FontFaceSet.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
 InspectorFontFace::FromFontGroup()
 {
@@ -48,33 +49,45 @@ InspectorFontFace::GetName(nsAString& aN
 }
 
 void
 InspectorFontFace::GetCSSFamilyName(nsAString& aCSSFamilyName)
 {
   aCSSFamilyName = mFontEntry->FamilyName();
 }
 
-nsCSSFontFaceRule*
+ServoFontFaceRule*
 InspectorFontFace::GetRule()
 {
-  // check whether this font entry is associated with an @font-face rule
-  // in the relevant font group's user font set
-  nsCSSFontFaceRule* rule = nullptr;
-  if (mFontEntry->IsUserFont()) {
-    FontFaceSet::UserFontSet* fontSet =
-      static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
-    if (fontSet) {
-      FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
-      if (fontFaceSet) {
-        rule = fontFaceSet->FindRuleForEntry(mFontEntry);
+  if (!mRule) {
+    // check whether this font entry is associated with an @font-face rule
+    // in the relevant font group's user font set
+    RawServoFontFaceRule* rule = nullptr;
+    if (mFontEntry->IsUserFont()) {
+      FontFaceSet::UserFontSet* fontSet =
+        static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
+      if (fontSet) {
+        FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
+        if (fontFaceSet) {
+          rule = fontFaceSet->FindRuleForEntry(mFontEntry);
+        }
       }
     }
+    if (rule) {
+      // XXX It would be better if we can share this with CSSOM tree,
+      // but that may require us to create another map, which is not
+      // great either. As far as they would use the same backend, and
+      // we don't really support mutating @font-face rule via CSSOM,
+      // it's probably fine for now.
+      uint32_t line, column;
+      Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
+      mRule = new ServoFontFaceRule(do_AddRef(rule), line, column);
+    }
   }
-  return rule;
+  return mRule;
 }
 
 int32_t
 InspectorFontFace::SrcIndex()
 {
   if (mFontEntry->IsUserFont()) {
     NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
     return mFontEntry->mUserFontData->mSrcIndex;
--- a/layout/inspector/InspectorFontFace.h
+++ b/layout/inspector/InspectorFontFace.h
@@ -2,23 +2,23 @@
 /* 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/. */
 
 #ifndef mozilla_InspectorFontFace_h
 #define mozilla_InspectorFontFace_h
 
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/dom/InspectorUtilsBinding.h"
 #include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "nsRange.h"
 
 class gfxFontEntry;
 class gfxFontGroup;
-class nsCSSFontFaceRule;
 
 namespace mozilla {
 namespace dom {
 
 /**
  * Information on font face usage by a given DOM Range, as returned by
  * InspectorUtils.getUsedFontFaces.
  */
@@ -49,17 +49,17 @@ public:
   }
 
   // Web IDL
   bool FromFontGroup();
   bool FromLanguagePrefs();
   bool FromSystemFallback();
   void GetName(nsAString& aName);
   void GetCSSFamilyName(nsAString& aCSSFamilyName);
-  nsCSSFontFaceRule* GetRule();
+  ServoFontFaceRule* GetRule();
   int32_t SrcIndex();
   void GetURI(nsAString& aURI);
   void GetLocalName(nsAString& aLocalName);
   void GetFormat(nsAString& aFormat);
   void GetMetadata(nsAString& aMetadata);
 
   void GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult,
                         ErrorResult& aRV);
@@ -75,16 +75,17 @@ public:
                   JS::MutableHandle<JSObject*> aReflector)
   {
     return InspectorFontFaceBinding::Wrap(aCx, this, aGivenProto, aReflector);
   }
 
 protected:
   RefPtr<gfxFontEntry> mFontEntry;
   RefPtr<gfxFontGroup> mFontGroup;
+  RefPtr<ServoFontFaceRule> mRule;
   uint8_t mMatchType;
 
   nsTArray<RefPtr<nsRange>> mRanges;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -8,20 +8,21 @@
 
 #include <algorithm>
 #include "mozilla/dom/FontFaceBinding.h"
 #include "mozilla/dom/FontFaceSet.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/ServoBindings.h"
 #include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsCSSParser.h"
 #include "nsIDocument.h"
 #include "nsStyleUtil.h"
 #include "StylePrefs.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -72,25 +73,23 @@ GetDataFrom(const T& aObject, uint8_t*& 
 
 // -- FontFace ---------------------------------------------------------------
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFace)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FontFace)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoaded)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRule)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOtherFontFaceSets)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FontFace)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoaded)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRule)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOtherFontFaceSets)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FontFace)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -151,17 +150,17 @@ LoadStateToStatus(gfxUserFontEntry::User
   }
   NS_NOTREACHED("invalid aLoadState value");
   return FontFaceLoadStatus::Error;
 }
 
 already_AddRefed<FontFace>
 FontFace::CreateForRule(nsISupports* aGlobal,
                         FontFaceSet* aFontFaceSet,
-                        nsCSSFontFaceRule* aRule)
+                        RawServoFontFaceRule* aRule)
 {
   RefPtr<FontFace> obj = new FontFace(aGlobal, aFontFaceSet);
   obj->mRule = aRule;
   obj->mSourceType = eSourceType_FontFaceRule;
   obj->mInFontFaceSet = true;
   return obj.forget();
 }
 
@@ -188,19 +187,18 @@ FontFace::Constructor(const GlobalObject
   obj->InitializeSource(aSource);
   return obj.forget();
 }
 
 void
 FontFace::InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource)
 {
   if (aSource.IsString()) {
-    if (!ParseDescriptor(eCSSFontDesc_Src,
-                         aSource.GetAsString(),
-                         mDescriptors->mSrc)) {
+    IgnoredErrorResult rv;
+    if (!SetDescriptor(eCSSFontDesc_Src, aSource.GetAsString(), rv)) {
       Reject(NS_ERROR_DOM_SYNTAX_ERR);
 
       SetStatus(FontFaceLoadStatus::Error);
       return;
     }
 
     mSourceType = eSourceType_URLs;
     return;
@@ -250,45 +248,45 @@ FontFace::SetFamily(const nsAString& aVa
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Family, aValue, aRv);
 }
 
 void
 FontFace::GetStyle(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Style, eCSSProperty_font_style, aResult);
+  GetDesc(eCSSFontDesc_Style, aResult);
 }
 
 void
 FontFace::SetStyle(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Style, aValue, aRv);
 }
 
 void
 FontFace::GetWeight(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Weight, eCSSProperty_font_weight, aResult);
+  GetDesc(eCSSFontDesc_Weight, aResult);
 }
 
 void
 FontFace::SetWeight(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Weight, aValue, aRv);
 }
 
 void
 FontFace::GetStretch(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Stretch, eCSSProperty_font_stretch, aResult);
+  GetDesc(eCSSFontDesc_Stretch, aResult);
 }
 
 void
 FontFace::SetStretch(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Stretch, aValue, aRv);
 }
@@ -296,17 +294,17 @@ FontFace::SetStretch(const nsAString& aV
 void
 FontFace::GetUnicodeRange(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
 
   // There is no eCSSProperty_unicode_range for us to pass in to GetDesc
   // to get a serialized (possibly defaulted) value, but that function
   // doesn't use the property ID for this descriptor anyway.
-  GetDesc(eCSSFontDesc_UnicodeRange, eCSSProperty_UNKNOWN, aResult);
+  GetDesc(eCSSFontDesc_UnicodeRange, aResult);
 }
 
 void
 FontFace::SetUnicodeRange(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_UnicodeRange, aValue, aRv);
 }
@@ -329,47 +327,45 @@ FontFace::SetVariant(const nsAString& aV
   // XXX Ignore assignments to variant until we support font-variant
   // descriptors (bug 1055385).
 }
 
 void
 FontFace::GetFeatureSettings(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_FontFeatureSettings, eCSSProperty_font_feature_settings,
-          aResult);
+  GetDesc(eCSSFontDesc_FontFeatureSettings, aResult);
 }
 
 void
 FontFace::SetFeatureSettings(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_FontFeatureSettings, aValue, aRv);
 }
 
 void
 FontFace::GetVariationSettings(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_FontVariationSettings, eCSSProperty_font_variation_settings,
-          aResult);
+  GetDesc(eCSSFontDesc_FontVariationSettings, aResult);
 }
 
 void
 FontFace::SetVariationSettings(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_FontVariationSettings, aValue, aRv);
 }
 
 void
 FontFace::GetDisplay(nsString& aResult)
 {
   mFontFaceSet->FlushUserFontSet();
-  GetDesc(eCSSFontDesc_Display, eCSSProperty_UNKNOWN, aResult);
+  GetDesc(eCSSFontDesc_Display, aResult);
 }
 
 void
 FontFace::SetDisplay(const nsAString& aValue, ErrorResult& aRv)
 {
   mFontFaceSet->FlushUserFontSet();
   SetDescriptor(eCSSFontDesc_Display, aValue, aRv);
 }
@@ -520,161 +516,120 @@ FontFace::DoReject(nsresult aResult)
     // See comments in Gecko_GetFontMetrics.
     ss->AppendTask(PostTraversalTask::RejectFontFaceLoadedPromise(this, aResult));
     return;
   }
 
   mLoaded->MaybeReject(aResult);
 }
 
-bool
-FontFace::ParseDescriptor(nsCSSFontDesc aDescID,
-                          const nsAString& aString,
-                          nsCSSValue& aResult)
+already_AddRefed<URLExtraData>
+FontFace::GetURLExtraData() const
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
   nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
   nsCOMPtr<nsIURI> docURI = window->GetDocumentURI();
   nsCOMPtr<nsIURI> base = window->GetDocBaseURI();
 
   RefPtr<URLExtraData> url = new URLExtraData(base, docURI, principal);
-  return ServoCSSParser::ParseFontDescriptor(aDescID, aString, url, aResult);
+  return url.forget();
 }
 
-void
+bool
 FontFace::SetDescriptor(nsCSSFontDesc aFontDesc,
                         const nsAString& aValue,
                         ErrorResult& aRv)
 {
+  // FIXME We probably don't need to distinguish between this anymore
+  // since we have common backend now.
   NS_ASSERTION(!HasRule(),
                "we don't handle rule backed FontFace objects yet");
   if (HasRule()) {
-    return;
+    return false;
   }
 
-  nsCSSValue parsedValue;
-  if (!ParseDescriptor(aFontDesc, aValue, parsedValue)) {
+  NS_ConvertUTF16toUTF8 value(aValue);
+  RefPtr<URLExtraData> url = GetURLExtraData();
+  if (!Servo_FontFaceRule_SetDescriptor(GetData(), aFontDesc, &value, url)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return;
+    return false;
   }
 
-  mDescriptors->Get(aFontDesc) = parsedValue;
-
   if (aFontDesc == eCSSFontDesc_UnicodeRange) {
     mUnicodeRangeDirty = true;
   }
 
   // XXX Setting descriptors doesn't actually have any effect on FontFace
   // objects that have started loading or have already been loaded.
+  return true;
 }
 
 bool
 FontFace::SetDescriptors(const nsAString& aFamily,
                          const FontFaceDescriptors& aDescriptors)
 {
   MOZ_ASSERT(!HasRule());
   MOZ_ASSERT(!mDescriptors);
 
-  mDescriptors = new CSSFontFaceDescriptors;
+  IgnoredErrorResult rv;
+  mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume();
 
   // Parse all of the mDescriptors in aInitializer, which are the values
   // we got from the JS constructor.
-  if (!ParseDescriptor(eCSSFontDesc_Family,
-                       aFamily,
-                       mDescriptors->mFamily) ||
-      *mDescriptors->mFamily.GetStringBufferValue() == 0 ||
-      !ParseDescriptor(eCSSFontDesc_Style,
-                       aDescriptors.mStyle,
-                       mDescriptors->mStyle) ||
-      !ParseDescriptor(eCSSFontDesc_Weight,
-                       aDescriptors.mWeight,
-                       mDescriptors->mWeight) ||
-      !ParseDescriptor(eCSSFontDesc_Stretch,
-                       aDescriptors.mStretch,
-                       mDescriptors->mStretch) ||
-      !ParseDescriptor(eCSSFontDesc_UnicodeRange,
-                       aDescriptors.mUnicodeRange,
-                       mDescriptors->mUnicodeRange) ||
-      !ParseDescriptor(eCSSFontDesc_FontFeatureSettings,
-                       aDescriptors.mFeatureSettings,
-                       mDescriptors->mFontFeatureSettings) ||
+  if (!SetDescriptor(eCSSFontDesc_Family, aFamily, rv) ||
+      !SetDescriptor(eCSSFontDesc_Style, aDescriptors.mStyle, rv) ||
+      !SetDescriptor(eCSSFontDesc_Weight, aDescriptors.mWeight, rv) ||
+      !SetDescriptor(eCSSFontDesc_Stretch, aDescriptors.mStretch, rv) ||
+      !SetDescriptor(eCSSFontDesc_UnicodeRange,
+                     aDescriptors.mUnicodeRange, rv) ||
+      !SetDescriptor(eCSSFontDesc_FontFeatureSettings,
+                     aDescriptors.mFeatureSettings, rv) ||
       (StylePrefs::sFontVariationsEnabled &&
-       !ParseDescriptor(eCSSFontDesc_FontVariationSettings,
-                        aDescriptors.mVariationSettings,
-                        mDescriptors->mFontVariationSettings)) ||
-      !ParseDescriptor(eCSSFontDesc_Display,
-                       aDescriptors.mDisplay,
-                       mDescriptors->mDisplay)) {
+       !SetDescriptor(eCSSFontDesc_FontVariationSettings,
+                      aDescriptors.mVariationSettings, rv)) ||
+      !SetDescriptor(eCSSFontDesc_Display, aDescriptors.mDisplay, rv)) {
     // XXX Handle font-variant once we support it (bug 1055385).
 
     // If any of the descriptors failed to parse, none of them should be set
     // on the FontFace.
-    mDescriptors = new CSSFontFaceDescriptors;
+    mDescriptors = Servo_FontFaceRule_CreateEmpty().Consume();
 
     Reject(NS_ERROR_DOM_SYNTAX_ERR);
 
     SetStatus(FontFaceLoadStatus::Error);
     return false;
   }
 
   return true;
 }
 
 void
 FontFace::GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const
 {
-  if (HasRule()) {
-    MOZ_ASSERT(mRule);
-    MOZ_ASSERT(!mDescriptors);
-    mRule->GetDesc(aDescID, aResult);
-  } else {
-    aResult = mDescriptors->Get(aDescID);
-  }
+  aResult.Reset();
+  Servo_FontFaceRule_GetDescriptor(GetData(), aDescID, &aResult);
 }
 
 void
-FontFace::GetDesc(nsCSSFontDesc aDescID,
-                  nsCSSPropertyID aPropID,
-                  nsString& aResult) const
+FontFace::GetDesc(nsCSSFontDesc aDescID, nsString& aResult) const
 {
-  MOZ_ASSERT(aDescID == eCSSFontDesc_UnicodeRange ||
-             aDescID == eCSSFontDesc_Display ||
-             aPropID != eCSSProperty_UNKNOWN,
-             "only pass eCSSProperty_UNKNOWN for eCSSFontDesc_UnicodeRange");
-
-  nsCSSValue value;
-  GetDesc(aDescID, value);
-
-  aResult.Truncate();
+  Servo_FontFaceRule_GetDescriptorCssText(GetData(), aDescID, &aResult);
 
   // Fill in a default value for missing descriptors.
-  if (value.GetUnit() == eCSSUnit_Null) {
+  if (aResult.IsEmpty()) {
     if (aDescID == eCSSFontDesc_UnicodeRange) {
       aResult.AssignLiteral("U+0-10FFFF");
     } else if (aDescID == eCSSFontDesc_Display) {
       aResult.AssignLiteral("auto");
     } else if (aDescID != eCSSFontDesc_Family &&
                aDescID != eCSSFontDesc_Src) {
       aResult.AssignLiteral("normal");
     }
-    return;
-  }
-
-  if (aDescID == eCSSFontDesc_UnicodeRange) {
-    // Since there's no unicode-range property, we can't use
-    // nsCSSValue::AppendToString to serialize this descriptor.
-    nsStyleUtil::AppendUnicodeRange(value, aResult);
-  } else if (aDescID == eCSSFontDesc_Display) {
-    AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(value.GetIntValue(),
-                                                  nsCSSProps::kFontDisplayKTable),
-                       aResult);
-  } else {
-    value.AppendToString(aPropID, aResult);
   }
 }
 
 void
 FontFace::SetUserFontEntry(gfxUserFontEntry* aEntry)
 {
   if (mUserFontEntry) {
     mUserFontEntry->mFontFaces.RemoveElement(this);
@@ -723,18 +678,17 @@ FontFace::GetFamilyName(nsString& aResul
 }
 
 void
 FontFace::DisconnectFromRule()
 {
   MOZ_ASSERT(HasRule());
 
   // Make a copy of the descriptors.
-  mDescriptors = new CSSFontFaceDescriptors;
-  mRule->GetDescriptors(*mDescriptors);
+  mDescriptors = Servo_FontFaceRule_Clone(mRule).Consume();
   mRule = nullptr;
   mInFontFaceSet = false;
 }
 
 bool
 FontFace::HasFontData() const
 {
   return mSourceType == eSourceType_Buffer && mSourceBuffer;
--- a/layout/style/FontFace.h
+++ b/layout/style/FontFace.h
@@ -10,21 +10,22 @@
 #include "mozilla/dom/FontFaceBinding.h"
 #include "gfxUserFontSet.h"
 #include "nsAutoPtr.h"
 #include "nsCSSPropertyID.h"
 #include "nsCSSValue.h"
 #include "nsWrapperCache.h"
 
 class gfxFontFaceBufferSource;
-class nsCSSFontFaceRule;
+struct RawServoFontFaceRule;
 
 namespace mozilla {
 struct CSSFontFaceDescriptors;
 class PostTraversalTask;
+class ServoFontFaceRule;
 namespace dom {
 class FontFaceBufferSource;
 struct FontFaceDescriptors;
 class FontFaceSet;
 class Promise;
 class StringOrArrayBufferOrArrayBufferView;
 } // namespace dom
 } // namespace mozilla
@@ -74,19 +75,19 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FontFace)
 
   nsISupports* GetParentObject() const { return mParent; }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<FontFace>
   CreateForRule(nsISupports* aGlobal, FontFaceSet* aFontFaceSet,
-                nsCSSFontFaceRule* aRule);
+                RawServoFontFaceRule* aRule);
 
-  nsCSSFontFaceRule* GetRule() { return mRule; }
+  RawServoFontFaceRule* GetRule() { return mRule; }
 
   void GetDesc(nsCSSFontDesc aDescID, nsCSSValue& aResult) const;
 
   gfxUserFontEntry* CreateUserFontEntry();
   gfxUserFontEntry* GetUserFontEntry() const { return mUserFontEntry; }
   void SetUserFontEntry(gfxUserFontEntry* aEntry);
 
   /**
@@ -175,43 +176,40 @@ private:
   FontFace(nsISupports* aParent, FontFaceSet* aFontFaceSet);
   ~FontFace();
 
   void InitializeSource(const StringOrArrayBufferOrArrayBufferView& aSource);
 
   // Helper function for Load.
   void DoLoad();
 
-  /**
-   * Parses a @font-face descriptor value, storing the result in aResult.
-   * Returns whether the parsing was successful.
-   */
-  bool ParseDescriptor(nsCSSFontDesc aDescID, const nsAString& aString,
-                       nsCSSValue& aResult);
-
   // Helper function for the descriptor setter methods.
-  void SetDescriptor(nsCSSFontDesc aFontDesc,
+  // Returns whether it successfully sets the descriptor.
+  bool SetDescriptor(nsCSSFontDesc aFontDesc,
                      const nsAString& aValue,
                      mozilla::ErrorResult& aRv);
 
   /**
    * Sets all of the descriptor values in mDescriptors using values passed
    * to the JS constructor.
    */
   bool SetDescriptors(const nsAString& aFamily,
                       const FontFaceDescriptors& aDescriptors);
 
   /**
    * Sets the current loading status.
    */
   void SetStatus(mozilla::dom::FontFaceLoadStatus aStatus);
 
-  void GetDesc(nsCSSFontDesc aDescID,
-               nsCSSPropertyID aPropID,
-               nsString& aResult) const;
+  void GetDesc(nsCSSFontDesc aDescID, nsString& aResult) const;
+
+  already_AddRefed<URLExtraData> GetURLExtraData() const;
+
+  RawServoFontFaceRule* GetData() const
+    { return HasRule() ? mRule : mDescriptors; }
 
   /**
    * Returns and takes ownership of the buffer storing the font data.
    */
   void TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength);
 
   // Acts like mLoaded->MaybeReject(aResult), except it doesn't create mLoaded
   // if it doesn't already exist.
@@ -231,17 +229,17 @@ private:
   // when JS asks for it.
   RefPtr<mozilla::dom::Promise> mLoaded;
 
   // Saves the rejection code for mLoaded if mLoaded hasn't been created yet.
   nsresult mLoadedRejection;
 
   // The @font-face rule this FontFace object is reflecting, if it is a
   // rule backed FontFace.
-  RefPtr<nsCSSFontFaceRule> mRule;
+  RefPtr<RawServoFontFaceRule> mRule;
 
   // The FontFace object's user font entry.  This is initially null, but is set
   // during FontFaceSet::UpdateRules and when a FontFace is explicitly loaded.
   RefPtr<Entry> mUserFontEntry;
 
   // The current load status of the font represented by this FontFace.
   // Note that we can't just reflect the value of the gfxUserFontEntry's
   // status, since the spec sometimes requires us to go through the event
@@ -261,17 +259,20 @@ private:
   // If the FontFace was constructed with an ArrayBuffer(View), this is a
   // copy of the data from it.
   uint8_t* mSourceBuffer;
   uint32_t mSourceBufferLength;
 
   // The values corresponding to the font face descriptors, if we are not
   // a rule backed FontFace object.  For rule backed objects, we use
   // the descriptors stored in mRule.
-  nsAutoPtr<mozilla::CSSFontFaceDescriptors> mDescriptors;
+  // FIXME This should hold a unique ptr to just the descriptors inside,
+  // so that we don't need to create a rule for it and don't need to
+  // assign a fake line number and column number. See bug 1450904.
+  RefPtr<RawServoFontFaceRule> mDescriptors;
 
   // The value of the unicode-range descriptor as a gfxCharacterMap.  Valid
   // only when mUnicodeRangeDirty is false.
   RefPtr<gfxCharacterMap> mUnicodeRange;
 
   // The primary FontFaceSet this FontFace is associated with,
   // regardless of whether it is currently "in" the set.
   RefPtr<FontFaceSet> mFontFaceSet;
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/FontFaceSetLoadEvent.h"
 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/LoadInfo.h"
 #include "nsAutoPtr.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCSSParser.h"
@@ -673,17 +674,17 @@ FontFaceSet::UpdateRules(const nsTArray<
   MOZ_ASSERT(mUserFontSet);
 
   // If there was a change to the mNonRuleFaces array, then there could
   // have been a modification to the user font set.
   bool modified = mNonRuleFacesDirty;
   mNonRuleFacesDirty = false;
 
   // reuse existing FontFace objects mapped to rules already
-  nsDataHashtable<nsPtrHashKey<nsCSSFontFaceRule>, FontFace*> ruleFaceMap;
+  nsDataHashtable<nsPtrHashKey<RawServoFontFaceRule>, FontFace*> ruleFaceMap;
   for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
     FontFace* f = mRuleFaces[i].mFontFace;
     if (!f) {
       continue;
     }
     ruleFaceMap.Put(f->GetRule(), f);
   }
 
@@ -704,24 +705,24 @@ FontFaceSet::UpdateRules(const nsTArray<
   for (auto it = mUserFontSet->mFontFamilies.Iter(); !it.Done(); it.Next()) {
     it.Data()->DetachFontEntries();
   }
 
   // Sometimes aRules has duplicate @font-face rules in it; we should make
   // that not happen, but in the meantime, don't try to insert the same
   // FontFace object more than once into mRuleFaces.  We track which
   // ones we've handled in this table.
-  nsTHashtable<nsPtrHashKey<nsCSSFontFaceRule>> handledRules;
+  nsTHashtable<nsPtrHashKey<RawServoFontFaceRule>> handledRules;
 
   for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
     // Insert each FontFace objects for each rule into our list, migrating old
     // font entries if possible rather than creating new ones; set  modified  to
     // true if we detect that rule ordering has changed, or if a new entry is
     // created.
-    nsCSSFontFaceRule* rule = aRules[i].mRule;
+    RawServoFontFaceRule* rule = aRules[i].mRule;
     if (!handledRules.EnsureInserted(rule)) {
       // rule was already present in the hashtable
       continue;
     }
     RefPtr<FontFace> f = ruleFaceMap.Get(rule);
     if (!f.get()) {
       f = FontFace::CreateForRule(GetParentObject(), this, rule);
     }
@@ -1076,17 +1077,19 @@ FontFaceSet::FindOrCreateUserFontEntryFr
 
     face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
     face->mBuffer = aFontFace->CreateBufferSource();
     face->mReferrerPolicy = mozilla::net::RP_Unset;
   } else {
     aFontFace->GetDesc(eCSSFontDesc_Src, val);
     unit = val.GetUnit();
     if (unit == eCSSUnit_Array) {
-      nsCSSValue::Array* srcArr = val.GetArrayValue();
+      // Hold a strong reference because content of val is going away
+      // in the loop below.
+      RefPtr<nsCSSValue::Array> srcArr = val.GetArrayValue();
       size_t numSrc = srcArr->Count();
 
       for (size_t i = 0; i < numSrc; i++) {
         val = srcArr->Item(i);
         unit = val.GetUnit();
         gfxFontFaceSrc* face = srcArray.AppendElements(1);
         if (!face)
           return nullptr;
@@ -1190,31 +1193,31 @@ FontFaceSet::FindOrCreateUserFontEntryFr
                                                  stretch, italicStyle,
                                                  featureSettings,
                                                  variationSettings,
                                                  languageOverride,
                                                  unicodeRanges, fontDisplay);
   return entry.forget();
 }
 
-nsCSSFontFaceRule*
+RawServoFontFaceRule*
 FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
 {
   NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
   for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
     FontFace* f = mRuleFaces[i].mFontFace;
     gfxUserFontEntry* entry = f->GetUserFontEntry();
     if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
       return f->GetRule();
     }
   }
   return nullptr;
 }
 
-nsCSSFontFaceRule*
+RawServoFontFaceRule*
 FontFaceSet::FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry)
 {
   for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
     FontFace* f = mRuleFaces[i].mFontFace;
     if (f->GetUserFontEntry() == aUserFontEntry) {
       return f->GetRule();
     }
   }
@@ -1278,34 +1281,38 @@ FontFaceSet::LogMessage(gfxUserFontEntry
   message.AppendLiteral(" source: ");
   message.Append(fontURI);
 
   if (LOG_ENABLED()) {
     LOG(("userfonts (%p) %s", mUserFontSet.get(), message.get()));
   }
 
   // try to give the user an indication of where the rule came from
-  nsCSSFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
+  RawServoFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
   nsString href;
   nsString text;
   uint32_t line = 0;
   uint32_t column = 0;
   if (rule) {
-    rule->GetCssText(text);
+    Servo_FontFaceRule_GetCssText(rule, &text);
+    Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
+    // FIXME We need to figure out an approach to get the style sheet
+    // of this raw rule. See bug 1450903.
+#if 0
     StyleSheet* sheet = rule->GetStyleSheet();
     // if the style sheet is removed while the font is loading can be null
     if (sheet) {
       nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
       CopyUTF8toUTF16(spec, href);
     } else {
       NS_WARNING("null parent stylesheet for @font-face rule");
       href.AssignLiteral("unknown");
     }
-    line = rule->GetLineNumber();
-    column = rule->GetColumnNumber();
+#endif
+    href.AssignLiteral("unknown");
   }
 
   nsresult rv;
   nsCOMPtr<nsIScriptError> scriptError =
     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint64_t innerWindowID = mDocument->InnerWindowID();
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -6,25 +6,25 @@
 
 #ifndef mozilla_dom_FontFaceSet_h
 #define mozilla_dom_FontFaceSet_h
 
 #include "mozilla/dom/FontFace.h"
 #include "mozilla/dom/FontFaceSetBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "gfxUserFontSet.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsICSSLoaderObserver.h"
 
 struct gfxFontFaceSrc;
 class gfxFontSrcPrincipal;
 class gfxUserFontEntry;
 class nsFontFaceLoader;
 class nsIPrincipal;
 class nsPIDOMWindowInner;
+struct RawServoFontFaceRule;
 
 namespace mozilla {
 class PostTraversalTask;
 class SharedFontList;
 namespace dom {
 class FontFace;
 class Promise;
 } // namespace dom
@@ -123,17 +123,17 @@ public:
   // It's removed from the mLoaders set.
   void RemoveLoader(nsFontFaceLoader* aLoader);
 
   bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
 
   nsPresContext* GetPresContext();
 
   // search for @font-face rule that matches a platform font entry
-  nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
+  RawServoFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
 
   void IncrementGeneration(bool aIsRebuild = false);
 
   /**
    * Finds an existing entry in the user font cache or creates a new user
    * font entry for the given FontFace object.
    */
   static already_AddRefed<gfxUserFontEntry>
@@ -269,17 +269,17 @@ private:
   };
 
   static already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
                                                    const nsAString& aFamilyName,
                                                    FontFace* aFontFace,
                                                    SheetType aSheetType);
 
   // search for @font-face rule that matches a userfont font entry
-  nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
+  RawServoFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
 
   nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
                      const gfxFontFaceSrc* aFontFaceSrc);
   gfxFontSrcPrincipal* GetStandardFontLoadPrincipal();
   nsresult CheckFontLoad(const gfxFontFaceSrc* aFontFaceSrc,
                          gfxFontSrcPrincipal** aPrincipal,
                          bool* aBypassCache);
   bool IsFontLoadAllowed(nsIURI* aFontLocation,
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -17,8 +17,9 @@ SERVO_ARC_TYPE(KeyframesRule, RawServoKe
 SERVO_ARC_TYPE(MediaList, RawServoMediaList)
 SERVO_ARC_TYPE(MediaRule, RawServoMediaRule)
 SERVO_ARC_TYPE(NamespaceRule, RawServoNamespaceRule)
 SERVO_ARC_TYPE(PageRule, RawServoPageRule)
 SERVO_ARC_TYPE(SupportsRule, RawServoSupportsRule)
 SERVO_ARC_TYPE(DocumentRule, RawServoDocumentRule)
 SERVO_ARC_TYPE(FontFeatureValuesRule, RawServoFontFeatureValuesRule)
 SERVO_ARC_TYPE(RuleNode, RawServoRuleNode)
+SERVO_ARC_TYPE(FontFaceRule, RawServoFontFaceRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -257,21 +257,20 @@ BASIC_RULE_FUNCS(Import)
 BASIC_RULE_FUNCS_WITHOUT_GETTER(Keyframe)
 BASIC_RULE_FUNCS(Keyframes)
 GROUP_RULE_FUNCS(Media)
 BASIC_RULE_FUNCS(Namespace)
 BASIC_RULE_FUNCS(Page)
 GROUP_RULE_FUNCS(Supports)
 GROUP_RULE_FUNCS(Document)
 BASIC_RULE_FUNCS(FontFeatureValues)
+BASIC_RULE_FUNCS(FontFace)
 #undef GROUP_RULE_FUNCS
 #undef BASIC_RULE_FUNCS
 #undef BASIC_RULE_FUNCS_WITHOUT_GETTER
-SERVO_BINDING_FUNC(Servo_CssRules_GetFontFaceRuleAt, nsCSSFontFaceRule*,
-                   ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_CssRules_GetCounterStyleRuleAt, nsCSSCounterStyleRule*,
                    ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetStyle, RawServoDeclarationBlockStrong,
                    RawServoStyleRuleBorrowed rule)
 SERVO_BINDING_FUNC(Servo_StyleRule_SetStyle, void,
                    RawServoStyleRuleBorrowed rule,
                    RawServoDeclarationBlockBorrowed declarations)
 SERVO_BINDING_FUNC(Servo_StyleRule_GetSelectorText, void,
@@ -338,16 +337,41 @@ SERVO_BINDING_FUNC(Servo_SupportsRule_Ge
 SERVO_BINDING_FUNC(Servo_DocumentRule_GetConditionText, void,
                    RawServoDocumentRuleBorrowed rule, nsAString* result)
 SERVO_BINDING_FUNC(Servo_FontFeatureValuesRule_GetFontFamily, void,
                    RawServoFontFeatureValuesRuleBorrowed rule,
                    nsAString* result)
 SERVO_BINDING_FUNC(Servo_FontFeatureValuesRule_GetValueText, void,
                    RawServoFontFeatureValuesRuleBorrowed rule,
                    nsAString* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_CreateEmpty, RawServoFontFaceRuleStrong)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_Clone, RawServoFontFaceRuleStrong,
+                   RawServoFontFaceRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetSourceLocation, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   uint32_t* line, uint32_t* column)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_Length, uint32_t,
+                   RawServoFontFaceRuleBorrowed rule)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_IndexGetter, nsCSSFontDesc,
+                   RawServoFontFaceRuleBorrowed rule, uint32_t index)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetDeclCssText, void,
+                   RawServoFontFaceRuleBorrowed rule, nsAString* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetDescriptor, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc, nsCSSValue* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_GetDescriptorCssText, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc, nsAString* result)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_SetDescriptor, bool,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc, const nsACString* value,
+                   RawGeckoURLExtraData* data)
+SERVO_BINDING_FUNC(Servo_FontFaceRule_ResetDescriptor, void,
+                   RawServoFontFaceRuleBorrowed rule,
+                   nsCSSFontDesc desc)
 
 // Animations API
 SERVO_BINDING_FUNC(Servo_ParseProperty,
                    RawServoDeclarationBlockStrong,
                    nsCSSPropertyID property, const nsACString* value,
                    RawGeckoURLExtraData* data,
                    mozilla::ParsingMode parsing_mode,
                    nsCompatibility quirks_mode,
@@ -800,21 +824,16 @@ SERVO_BINDING_FUNC(Servo_ParseTransformI
                    RawGeckoGfxMatrix4x4* result);
 SERVO_BINDING_FUNC(Servo_ParseCounterStyleName, nsAtom*,
                    const nsACString* value);
 SERVO_BINDING_FUNC(Servo_ParseCounterStyleDescriptor, bool,
                    nsCSSCounterDesc aDescriptor,
                    const nsACString* aValue,
                    RawGeckoURLExtraData* aURLExtraData,
                    nsCSSValue* aResult);
-SERVO_BINDING_FUNC(Servo_ParseFontDescriptor, bool,
-                   nsCSSFontDesc desc_id,
-                   const nsAString* value,
-                   RawGeckoURLExtraData* data,
-                   nsCSSValueBorrowedMut);
 SERVO_BINDING_FUNC(Servo_ParseFontShorthandForMatching, bool,
                    const nsAString* value,
                    RawGeckoURLExtraData* data,
                    RefPtr<SharedFontList>* family,
                    nsCSSValueBorrowedMut style,
                    nsCSSValueBorrowedMut stretch,
                    nsCSSValueBorrowedMut weight);
 
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_ServoBindingTypes_h
 #define mozilla_ServoBindingTypes_h
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/ServoTypes.h"
+#include "mozilla/SheetType.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/Types.h"
 #include "nsCSSPropertyID.h"
 #include "nsStyleAutoArray.h"
 #include "nsTArray.h"
 
 struct RawServoAuthorStyles;
 struct RawServoStyleSet;
@@ -231,9 +232,16 @@ 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);
 
 #undef DEFINE_BOXED_TYPE
 
+// used for associating sheet type with specific @font-face rules
+struct nsFontFaceRuleContainer
+{
+  RefPtr<RawServoFontFaceRule> mRule;
+  mozilla::SheetType mSheetType;
+};
+
 #endif // mozilla_ServoBindingTypes_h
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -9,17 +9,16 @@
 #include "ChildIterator.h"
 #include "ErrorReporter.h"
 #include "GeckoProfiler.h"
 #include "gfxFontFamilyList.h"
 #include "gfxFontFeatures.h"
 #include "nsAnimationManager.h"
 #include "nsAttrValueInlines.h"
 #include "nsCSSCounterStyleRule.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
 #include "nsCSSParser.h"
 #include "nsCSSPseudoElements.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsDeviceContext.h"
 #include "nsIContentInlines.h"
@@ -2573,43 +2572,16 @@ Gecko_CSSKeywordString(nsCSSKeyword aKey
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aLength);
   const nsCString& value = nsCSSKeywords::GetStringValue(aKeyword);
   *aLength = value.Length();
   return value.get();
 }
 
-nsCSSFontFaceRule*
-Gecko_CSSFontFaceRule_Create(uint32_t aLine, uint32_t aColumn)
-{
-  RefPtr<nsCSSFontFaceRule> rule = new nsCSSFontFaceRule(aLine, aColumn);
-  return rule.forget().take();
-}
-
-nsCSSFontFaceRule*
-Gecko_CSSFontFaceRule_Clone(const nsCSSFontFaceRule* aRule)
-{
-  RefPtr<css::Rule> rule = aRule->Clone();
-  return static_cast<nsCSSFontFaceRule*>(rule.forget().take());
-}
-
-void
-Gecko_CSSFontFaceRule_GetCssText(const nsCSSFontFaceRule* aRule,
-                                 nsAString* aResult)
-{
-  // GetCSSText serializes nsCSSValues, which have a heap write
-  // 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);
 }
 
 int32_t
@@ -2624,18 +2596,16 @@ Gecko_RegisterNamespace(nsAtom* aNamespa
   nsresult rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(str, id);
 
   if (NS_FAILED(rv)) {
     return -1;
   }
   return id;
 }
 
-NS_IMPL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule);
-
 nsCSSCounterStyleRule*
 Gecko_CSSCounterStyle_Create(nsAtom* aName)
 {
   RefPtr<nsCSSCounterStyleRule> rule = new nsCSSCounterStyleRule(aName, 0, 0);
   return rule.forget().take();
 }
 
 nsCSSCounterStyleRule*
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -58,17 +58,16 @@ namespace mozilla {
   class ServoElementSnapshotTable;
 }
 using mozilla::FontFamilyList;
 using mozilla::FontFamilyName;
 using mozilla::FontFamilyType;
 using mozilla::ServoElementSnapshot;
 using mozilla::SharedFontList;
 class nsCSSCounterStyleRule;
-class nsCSSFontFaceRule;
 struct nsMediaFeature;
 class nsSimpleContentList;
 struct nsStyleList;
 struct nsStyleImage;
 struct nsStyleGradientStop;
 class nsStyleGradient;
 class nsStyleCoord;
 struct nsStyleDisplay;
@@ -624,23 +623,16 @@ mozilla::ServoStyleSheet* Gecko_StyleShe
     const mozilla::ServoStyleSheet* aSheet,
     const mozilla::ServoStyleSheet* aNewParentSheet);
 void Gecko_StyleSheet_AddRef(const mozilla::ServoStyleSheet* aSheet);
 void Gecko_StyleSheet_Release(const mozilla::ServoStyleSheet* aSheet);
 
 nsCSSKeyword Gecko_LookupCSSKeyword(const uint8_t* string, uint32_t len);
 const char* Gecko_CSSKeywordString(nsCSSKeyword keyword, uint32_t* len);
 
-// Font face rule
-// Creates and returns a new (already-addrefed) nsCSSFontFaceRule object.
-nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Create(uint32_t line, uint32_t column);
-nsCSSFontFaceRule* Gecko_CSSFontFaceRule_Clone(const nsCSSFontFaceRule* rule);
-void Gecko_CSSFontFaceRule_GetCssText(const nsCSSFontFaceRule* rule, nsAString* result);
-NS_DECL_FFI_REFCOUNTING(nsCSSFontFaceRule, CSSFontFaceRule);
-
 // Counter style rule
 // Creates and returns a new (already-addrefed) nsCSSCounterStyleRule object.
 nsCSSCounterStyleRule* Gecko_CSSCounterStyle_Create(nsAtom* name);
 nsCSSCounterStyleRule* Gecko_CSSCounterStyle_Clone(const nsCSSCounterStyleRule* rule);
 void Gecko_CSSCounterStyle_GetCssText(const nsCSSCounterStyleRule* rule, nsAString* result);
 NS_DECL_FFI_REFCOUNTING(nsCSSCounterStyleRule, CSSCounterStyleRule);
 
 bool Gecko_IsDocumentBody(RawGeckoElementBorrowed element);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -74,17 +74,16 @@ headers = [
     "mozilla/StylePrefs.h",
     "mozilla/ServoBindings.h",
     "mozilla/ServoMediaList.h",
     "mozilla/ComputedStyle.h",
     "mozilla/ServoDeclarationBlock.h",
     "mozilla/ServoTraversalStatistics.h",
     "mozilla/SizeOfState.h",
     "nsCSSCounterStyleRule.h",
-    "nsCSSFontFaceRule.h",
     "nsContentUtils.h",
     "nsNameSpaceManager.h",
     "nsMediaFeatures.h",
     "nsXBLBinding.h",
 ]
 raw-lines = [
     # FIXME(emilio): Incrementally remove these "pub use"s. Probably
     # mozilla::css and mozilla::dom are easier.
@@ -276,17 +275,16 @@ whitelist-types = [
     "MediumFeaturesChangedResult",
     "nsAttrName",
     "nsAttrValue",
     "nscolor",
     "nsChangeHint",
     "nsCSSCounterDesc",
     "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
-    "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSProps",
     "nsCSSRect",
     "nsCSSRect_heap",
     "nsCSSShadowArray",
     "nsCSSValue",
@@ -492,16 +490,17 @@ structs-types = [
     "RawGeckoElement",
     "Element",
     "RawGeckoKeyframeList",
     "RawGeckoPropertyValuePairList",
     "RawGeckoComputedKeyframeValuesList",
     "RawGeckoFontFaceRuleList",
     "RawGeckoNode",
     "RawServoAnimationValue",
+    "RawServoFontFaceRule",
     "RawGeckoServoAnimationValueList",
     "RawServoMediaList",
     "RawServoStyleSheetContents",
     "RawServoDeclarationBlock",
     "RawServoStyleRule",
     "RawGeckoPresContext",
     "RawGeckoPresContextOwned",
     "RawGeckoStyleAnimationList",
@@ -531,17 +530,16 @@ structs-types = [
     "SheetParsingMode",
     "StyleBasicShapeType",
     "StyleShapeSource",
     "StyleTransition",
     "gfxFontFeatureValueSet",
     "nsCSSCounterDesc",
     "nsCSSCounterStyleRule",
     "nsCSSFontDesc",
-    "nsCSSFontFaceRule",
     "nsCSSKeyword",
     "nsCSSPropertyID",
     "nsCSSPropertyIDSet",
     "nsCSSRect",
     "nsCSSShadowArray",
     "nsCSSUnit",
     "nsCSSValue",
     "nsCSSValueSharedList",
--- a/layout/style/ServoCSSParser.cpp
+++ b/layout/style/ServoCSSParser.cpp
@@ -88,25 +88,16 @@ ServoCSSParser::ParseTransformIntoMatrix
                                          RawGeckoGfxMatrix4x4& aResult)
 {
   return Servo_ParseTransformIntoMatrix(&aValue,
                                         &aContains3DTransform,
                                         &aResult);
 }
 
 /* static */ bool
-ServoCSSParser::ParseFontDescriptor(nsCSSFontDesc aDescID,
-                                    const nsAString& aValue,
-                                    URLExtraData* aUrl,
-                                    nsCSSValue& aResult)
-{
-  return Servo_ParseFontDescriptor(aDescID, &aValue, aUrl, &aResult);
-}
-
-/* static */ bool
 ServoCSSParser::ParseFontShorthandForMatching(const nsAString& aValue,
                                               URLExtraData* aUrl,
                                               RefPtr<SharedFontList>& aList,
                                               nsCSSValue& aStyle,
                                               nsCSSValue& aStretch,
                                               nsCSSValue& aWeight)
 {
   return Servo_ParseFontShorthandForMatching(&aValue, aUrl, &aList,
--- a/layout/style/ServoCSSParser.h
+++ b/layout/style/ServoCSSParser.h
@@ -147,30 +147,16 @@ public:
    * @param aResult The output matrix. (output)
    * @return Whether the value was successfully parsed.
    */
   static bool ParseTransformIntoMatrix(const nsAString& aValue,
                                        bool& aContains3DTransform,
                                        RawGeckoGfxMatrix4x4& aResult);
 
   /**
-   * Parse a font descriptor.
-   *
-   * @param aDescID The font descriptor id.
-   * @param aValue The specified value.
-   * @param aUrl The parser url extra data.
-   * @param aResult The parsed result. (output)
-   * @return Whether the value was successfully parsed.
-   */
-  static bool ParseFontDescriptor(nsCSSFontDesc aDescID,
-                                  const nsAString& aValue,
-                                  URLExtraData* aUrl,
-                                  nsCSSValue& aResult);
-
-  /**
    * Parse a font shorthand for FontFaceSet matching, so we only care about
    * FontFamily, FontStyle, FontStretch, and FontWeight.
    *
    * @param aValue The specified value.
    * @param aUrl The parser url extra data.
    * @param aList The parsed FontFamily list. (output)
    * @param aStyle The parsed FontStyle. (output)
    * @param aStretch The parsed FontStretch. (output)
--- a/layout/style/ServoCSSRuleList.cpp
+++ b/layout/style/ServoCSSRuleList.cpp
@@ -7,26 +7,26 @@
 /* representation of CSSRuleList for stylo */
 
 #include "mozilla/ServoCSSRuleList.h"
 
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoDocumentRule.h"
 #include "mozilla/ServoImportRule.h"
+#include "mozilla/ServoFontFaceRule.h"
 #include "mozilla/ServoFontFeatureValuesRule.h"
 #include "mozilla/ServoKeyframesRule.h"
 #include "mozilla/ServoMediaRule.h"
 #include "mozilla/ServoNamespaceRule.h"
 #include "mozilla/ServoPageRule.h"
 #include "mozilla/ServoStyleRule.h"
 #include "mozilla/ServoStyleSheet.h"
 #include "mozilla/ServoSupportsRule.h"
 #include "nsCSSCounterStyleRule.h"
-#include "nsCSSFontFaceRule.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 ServoCSSRuleList::ServoCSSRuleList(already_AddRefed<ServoCssRules> aRawRules,
                                    ServoStyleSheet* aDirectOwnerStyleSheet)
   : mStyleSheet(aDirectOwnerStyleSheet)
@@ -48,23 +48,21 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Se
   tmp->DropAllRules();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(dom::CSSRuleList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoCSSRuleList,
                                                   dom::CSSRuleList)
   tmp->EnumerateInstantiatedRules([&](css::Rule* aRule) {
     if (!aRule->IsCCLeaf()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
       cb.NoteXPCOMChild(aRule);
-      // Note about @font-face and @counter-style rule again, since
-      // there is an indirect owning edge through Servo's struct that
-      // FontFaceRule / CounterStyleRule in Servo owns a Gecko
-      // nsCSSFontFaceRule / nsCSSCounterStyleRule object.
+      // Note about @counter-style rule again, since there is an indirect owning
+      // edge through Servo's struct that CounterStyleRule in Servo owns a Gecko
+      // nsCSSCounterStyleRule object.
       auto type = aRule->Type();
-      if (type == CSSRuleBinding::FONT_FACE_RULE ||
-          type == CSSRuleBinding::COUNTER_STYLE_RULE) {
+      if (type == CSSRuleBinding::COUNTER_STYLE_RULE) {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRawRules[i]");
         cb.NoteXPCOMChild(aRule);
       }
     }
   });
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 void
@@ -107,25 +105,22 @@ ServoCSSRuleList::GetRule(uint32_t aInde
       CASE_RULE(KEYFRAMES, Keyframes)
       CASE_RULE(MEDIA, Media)
       CASE_RULE(NAMESPACE, Namespace)
       CASE_RULE(PAGE, Page)
       CASE_RULE(SUPPORTS, Supports)
       CASE_RULE(DOCUMENT, Document)
       CASE_RULE(IMPORT, Import)
       CASE_RULE(FONT_FEATURE_VALUES, FontFeatureValues)
+      CASE_RULE(FONT_FACE, FontFace)
 #undef CASE_RULE
-      // For @font-face and @counter-style rules, the function returns
-      // a borrowed Gecko rule object directly, so we don't need to
-      // create anything here. But we still need to have the style sheet
-      // and parent rule set properly.
-      case CSSRuleBinding::FONT_FACE_RULE: {
-        ruleObj = Servo_CssRules_GetFontFaceRuleAt(mRawRules, aIndex);
-        break;
-      }
+      // For @counter-style rules, the function returns a borrowed Gecko
+      // rule object directly, so we don't need to create anything here.
+      // But we still need to have the style sheet and parent rule set
+      // properly.
       case CSSRuleBinding::COUNTER_STYLE_RULE: {
         ruleObj = Servo_CssRules_GetCounterStyleRuleAt(mRawRules, aIndex);
         break;
       }
       case CSSRuleBinding::KEYFRAME_RULE:
         MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
         return nullptr;
       default:
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoFontFaceRule.cpp
@@ -0,0 +1,246 @@
+/* -*- 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/. */
+
+#include "mozilla/ServoFontFaceRule.h"
+
+#include "mozilla/dom/CSSFontFaceRuleBinding.h"
+#include "mozilla/dom/CSSStyleDeclarationBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+// -------------------------------------------
+// ServoFontFaceRuleDecl and related routines
+//
+
+// QueryInterface implementation for ServoFontFaceRuleDecl
+NS_INTERFACE_MAP_BEGIN(ServoFontFaceRuleDecl)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  // We forward the cycle collection interfaces to ContainingRule(), which is
+  // never null (in fact, we're part of that object!)
+  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
+      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
+    return ContainingRule()->QueryInterface(aIID, aInstancePtr);
+  }
+  else
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF_USING_AGGREGATOR(ServoFontFaceRuleDecl, ContainingRule())
+NS_IMPL_RELEASE_USING_AGGREGATOR(ServoFontFaceRuleDecl, ContainingRule())
+
+// helper for string GetPropertyValue and RemovePropertyValue
+void
+ServoFontFaceRuleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID,
+                                        nsAString & aResult) const
+{
+  aResult.Truncate();
+  Servo_FontFaceRule_GetDescriptorCssText(mRawRule, aFontDescID, &aResult);
+}
+
+void
+ServoFontFaceRuleDecl::GetCssText(nsAString& aCssText)
+{
+  Servo_FontFaceRule_GetDeclCssText(mRawRule, &aCssText);
+}
+
+void
+ServoFontFaceRuleDecl::SetCssText(const nsAString& aCssText,
+                                  nsIPrincipal* aSubjectPrincipal,
+                                  ErrorResult& aRv)
+{
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); // bug 443978
+}
+
+NS_IMETHODIMP
+ServoFontFaceRuleDecl::GetPropertyValue(const nsAString& aPropName,
+                                        nsAString& aResult)
+{
+  GetPropertyValue(nsCSSProps::LookupFontDesc(aPropName), aResult);
+  return NS_OK;
+}
+
+already_AddRefed<dom::CSSValue>
+ServoFontFaceRuleDecl::GetPropertyCSSValue(const nsAString& aPropName,
+                                           ErrorResult& aRv)
+{
+  // ??? nsDOMCSSDeclaration returns null/NS_OK, but that seems wrong.
+  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+  return nullptr;
+}
+
+NS_IMETHODIMP
+ServoFontFaceRuleDecl::RemoveProperty(const nsAString& aPropName,
+                                      nsAString& aResult)
+{
+  nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(aPropName);
+  NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN &&
+               descID < eCSSFontDesc_COUNT,
+               "LookupFontDesc returned value out of range");
+
+  if (descID == eCSSFontDesc_UNKNOWN) {
+    aResult.Truncate();
+  } else {
+    GetPropertyValue(descID, aResult);
+    Servo_FontFaceRule_ResetDescriptor(mRawRule, descID);
+  }
+  return NS_OK;
+}
+
+void
+ServoFontFaceRuleDecl::GetPropertyPriority(const nsAString& aPropName,
+                                           nsAString& aResult)
+{
+  // font descriptors do not have priorities at present
+  aResult.Truncate();
+}
+
+NS_IMETHODIMP
+ServoFontFaceRuleDecl::SetProperty(const nsAString& aPropName,
+                                   const nsAString& aValue,
+                                   const nsAString& aPriority,
+                                   nsIPrincipal* aSubjectPrincipal)
+{
+  // FIXME(heycam): If we are changing unicode-range, then a FontFace object
+  // representing this rule must have its mUnicodeRange value invalidated.
+
+  return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
+}
+
+uint32_t
+ServoFontFaceRuleDecl::Length()
+{
+  return Servo_FontFaceRule_Length(mRawRule);
+}
+
+void
+ServoFontFaceRuleDecl::IndexedGetter(uint32_t aIndex, bool& aFound,
+                                     nsAString& aResult)
+{
+  nsCSSFontDesc id = Servo_FontFaceRule_IndexGetter(mRawRule, aIndex);
+  if (id != eCSSFontDesc_UNKNOWN) {
+    aFound = true;
+    aResult.AssignASCII(nsCSSProps::GetStringValue(id).get());
+  } else {
+    aFound = false;
+  }
+}
+
+css::Rule*
+ServoFontFaceRuleDecl::GetParentRule()
+{
+  return ContainingRule();
+}
+
+nsINode*
+ServoFontFaceRuleDecl::GetParentObject()
+{
+  return ContainingRule()->GetDocument();
+}
+
+JSObject*
+ServoFontFaceRuleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
+{
+  return CSSStyleDeclarationBinding::Wrap(cx, this, aGivenProto);
+}
+
+// -------------------------------------------
+// ServoFontFaceRule
+//
+
+/* virtual */ already_AddRefed<css::Rule>
+ServoFontFaceRule::Clone() const
+{
+  RefPtr<RawServoFontFaceRule> rule = Servo_FontFaceRule_Clone(Raw()).Consume();
+  RefPtr<css::Rule> clone = new ServoFontFaceRule(rule.forget(),
+                                                  GetLineNumber(),
+                                                  GetColumnNumber());
+  return clone.forget();
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(ServoFontFaceRule)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ServoFontFaceRule,
+                                               mozilla::css::Rule)
+  // Keep this in sync with IsCCLeaf.
+
+  // Trace the wrapper for our declaration.  This just expands out
+  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
+  // directly because the wrapper is on the declaration, not on us.
+  tmp->mDecl.TraceWrapper(aCallbacks, aClosure);
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServoFontFaceRule,
+                                                mozilla::css::Rule)
+  // Keep this in sync with IsCCLeaf.
+
+  // Unlink the wrapper for our declaraton.  This just expands out
+  // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
+  // directly because the wrapper is on the declaration, not on us.
+  tmp->mDecl.ReleaseWrapper(static_cast<nsISupports*>(p));
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServoFontFaceRule,
+                                                  mozilla::css::Rule)
+  // Keep this in sync with IsCCLeaf.
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+bool
+ServoFontFaceRule::IsCCLeaf() const
+{
+  if (!Rule::IsCCLeaf()) {
+    return false;
+  }
+
+  return !mDecl.PreservingWrapper();
+}
+
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ServoFontFaceRule, mozilla::css::Rule)
+
+#ifdef DEBUG
+void
+ServoFontFaceRule::List(FILE* out, int32_t aIndent) const
+{
+  nsAutoCString str;
+  for (int32_t i = 0; i < aIndent; i++) {
+    str.AppendLiteral("  ");
+  }
+  Servo_FontFaceRule_Debug(Raw(), &str);
+  fprintf_stderr(out, "%s\n", str.get());
+}
+#endif
+
+uint16_t
+ServoFontFaceRule::Type() const
+{
+  return CSSRuleBinding::FONT_FACE_RULE;
+}
+
+void
+ServoFontFaceRule::GetCssText(nsAString& aCssText) const
+{
+  Servo_FontFaceRule_GetCssText(Raw(), &aCssText);
+}
+
+nsICSSDeclaration*
+ServoFontFaceRule::Style()
+{
+  return &mDecl;
+}
+
+/* virtual */ size_t
+ServoFontFaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this);
+}
+
+/* virtual */ JSObject*
+ServoFontFaceRule::WrapObject(JSContext* aCx,
+                              JS::Handle<JSObject*> aGivenProto)
+{
+  return CSSFontFaceRuleBinding::Wrap(aCx, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoFontFaceRule.h
@@ -0,0 +1,112 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ServoFontFaceRule_h
+#define mozilla_ServoFontFaceRule_h
+
+#include "mozilla/ServoBindingTypes.h"
+#include "mozilla/css/Rule.h"
+#include "nsICSSDeclaration.h"
+
+namespace mozilla {
+
+// A ServoFontFaceRuleDecl is always embeded in a ServoFontFaceRule.
+class ServoFontFaceRule;
+class ServoFontFaceRuleDecl final : public nsICSSDeclaration
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
+
+  already_AddRefed<dom::CSSValue>
+  GetPropertyCSSValue(const nsAString& aProp, ErrorResult& aRv) final;
+  using nsICSSDeclaration::GetPropertyCSSValue;
+
+  nsINode* GetParentObject() final;
+  void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) final;
+
+  void GetPropertyValue(nsCSSFontDesc aFontDescID, nsAString& aResult) const;
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+protected:
+  // For accessing the constructor.
+  friend class ServoFontFaceRule;
+
+  explicit ServoFontFaceRuleDecl(already_AddRefed<RawServoFontFaceRule> aDecl)
+    : mRawRule(Move(aDecl)) {}
+
+  ~ServoFontFaceRuleDecl() = default;
+
+  inline ServoFontFaceRule* ContainingRule();
+  inline const ServoFontFaceRule* ContainingRule() const;
+
+  RefPtr<RawServoFontFaceRule> mRawRule;
+
+private:
+  void* operator new(size_t size) CPP_THROW_NEW = delete;
+};
+
+class ServoFontFaceRule final : public css::Rule
+{
+public:
+  ServoFontFaceRule(already_AddRefed<RawServoFontFaceRule> aRawRule,
+                    uint32_t aLine, uint32_t aColumn)
+    : css::Rule(aLine, aColumn)
+    , mDecl(Move(aRawRule))
+  {}
+
+  ServoFontFaceRule(const ServoFontFaceRule&) = delete;
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
+      ServoFontFaceRule, css::Rule)
+  bool IsCCLeaf() const final;
+
+  RawServoFontFaceRule* Raw() const { return mDecl.mRawRule; }
+
+  // WebIDL interface
+  uint16_t Type() const final;
+  void GetCssText(nsAString& aCssText) const final;
+  nsICSSDeclaration* Style();
+
+  // Methods of mozilla::css::Rule
+  already_AddRefed<css::Rule> Clone() const final;
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+    const final;
+
+  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
+
+#ifdef DEBUG
+  void List(FILE* out = stdout, int32_t aIndent = 0) const final;
+#endif
+
+private:
+  virtual ~ServoFontFaceRule() = default;
+
+  // For computing the offset of mDecl.
+  friend class ServoFontFaceRuleDecl;
+
+  ServoFontFaceRuleDecl mDecl;
+};
+
+inline ServoFontFaceRule*
+ServoFontFaceRuleDecl::ContainingRule()
+{
+  return reinterpret_cast<ServoFontFaceRule*>
+    (reinterpret_cast<char*>(this) - offsetof(ServoFontFaceRule, mDecl));
+}
+
+inline const ServoFontFaceRule*
+ServoFontFaceRuleDecl::ContainingRule() const
+{
+  return reinterpret_cast<const ServoFontFaceRule*>
+    (reinterpret_cast<const char*>(this) - offsetof(ServoFontFaceRule, mDecl));
+}
+
+} // namespace mozilla
+
+#endif // mozilla_ServoFontFaceRule_h
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -27,17 +27,16 @@ EXPORTS += [
     'CounterStyleManager.h',
     'nsAnimationManager.h',
     'nsComputedDOMStylePropertyList.h',
     'nsCSSAnonBoxes.h',
     'nsCSSAnonBoxList.h',
     'nsCSSCounterDescList.h',
     'nsCSSCounterStyleRule.h',
     'nsCSSFontDescList.h',
-    'nsCSSFontFaceRule.h',
     'nsCSSKeywordList.h',
     'nsCSSKeywords.h',
     'nsCSSParser.h',
     'nsCSSPropAliasList.h',
     'nsCSSPropertyID.h',
     'nsCSSPropertyIDSet.h',
     'nsCSSPropList.h',
     'nsCSSProps.h',
@@ -85,16 +84,17 @@ EXPORTS.mozilla += [
     'ServoBindings.h',
     'ServoBindingTypes.h',
     'ServoCSSParser.h',
     'ServoCSSRuleList.h',
     'ServoDeclarationBlock.h',
     'ServoDocumentRule.h',
     'ServoElementSnapshot.h',
     'ServoElementSnapshotTable.h',
+    'ServoFontFaceRule.h',
     'ServoFontFeatureValuesRule.h',
     'ServoImportRule.h',
     'ServoKeyframeRule.h',
     'ServoKeyframesRule.h',
     'ServoMediaList.h',
     'ServoMediaRule.h',
     'ServoNamespaceRule.h',
     'ServoPageRule.h',
@@ -177,17 +177,16 @@ UNIFIED_SOURCES += [
     'ImageLoader.cpp',
     'LayerAnimationInfo.cpp',
     'Loader.cpp',
     'MediaList.cpp',
     'MediaQueryList.cpp',
     'nsAnimationManager.cpp',
     'nsComputedDOMStyle.cpp',
     'nsCSSCounterStyleRule.cpp',
-    'nsCSSFontFaceRule.cpp',
     'nsCSSKeywords.cpp',
     'nsCSSProps.cpp',
     'nsCSSScanner.cpp',
     'nsCSSValue.cpp',
     'nsDOMCSSAttrDeclaration.cpp',
     'nsDOMCSSDeclaration.cpp',
     'nsDOMCSSRect.cpp',
     'nsDOMCSSRGBColor.cpp',
@@ -208,16 +207,17 @@ UNIFIED_SOURCES += [
     'PreloadedStyleSheet.cpp',
     'Rule.cpp',
     'ServoBindings.cpp',
     'ServoCSSParser.cpp',
     'ServoCSSRuleList.cpp',
     'ServoDeclarationBlock.cpp',
     'ServoDocumentRule.cpp',
     'ServoElementSnapshot.cpp',
+    'ServoFontFaceRule.cpp',
     'ServoFontFeatureValuesRule.cpp',
     'ServoImportRule.cpp',
     'ServoKeyframeRule.cpp',
     'ServoKeyframesRule.cpp',
     'ServoMediaList.cpp',
     'ServoMediaRule.cpp',
     'ServoNamespaceRule.cpp',
     'ServoPageRule.cpp',
deleted file mode 100644
--- a/layout/style/nsCSSFontFaceRule.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
-/* -*- 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 Gecko @font-face rule */
-
-#include "nsCSSFontFaceRule.h"
-
-#include "mozilla/dom/CSSFontFaceRuleBinding.h"
-#include "mozilla/dom/CSSStyleDeclarationBinding.h"
-#include "nsStyleUtil.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-
-// -------------------------------------------
-// nsCSSFontFaceStyleDecl and related routines
-//
-
-// Mapping from nsCSSFontDesc codes to CSSFontFaceDescriptors fields.
-nsCSSValue CSSFontFaceDescriptors::* const
-CSSFontFaceDescriptors::Fields[] = {
-#define CSS_FONT_DESC(name_, method_) &CSSFontFaceDescriptors::m##method_,
-#include "nsCSSFontDescList.h"
-#undef CSS_FONT_DESC
-};
-
-const nsCSSValue&
-CSSFontFaceDescriptors::Get(nsCSSFontDesc aFontDescID) const
-{
-  MOZ_ASSERT(aFontDescID > eCSSFontDesc_UNKNOWN &&
-             aFontDescID < eCSSFontDesc_COUNT);
-  return this->*CSSFontFaceDescriptors::Fields[aFontDescID];
-}
-
-nsCSSValue&
-CSSFontFaceDescriptors::Get(nsCSSFontDesc aFontDescID)
-{
-  MOZ_ASSERT(aFontDescID > eCSSFontDesc_UNKNOWN &&
-             aFontDescID < eCSSFontDesc_COUNT);
-  return this->*CSSFontFaceDescriptors::Fields[aFontDescID];
-}
-
-// QueryInterface implementation for nsCSSFontFaceStyleDecl
-NS_INTERFACE_MAP_BEGIN(nsCSSFontFaceStyleDecl)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  // We forward the cycle collection interfaces to ContainingRule(), which is
-  // never null (in fact, we're part of that object!)
-  if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
-      aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
-    return ContainingRule()->QueryInterface(aIID, aInstancePtr);
-  }
-  else
-NS_INTERFACE_MAP_END
-
-NS_IMPL_ADDREF_USING_AGGREGATOR(nsCSSFontFaceStyleDecl, ContainingRule())
-NS_IMPL_RELEASE_USING_AGGREGATOR(nsCSSFontFaceStyleDecl, ContainingRule())
-
-// helper for string GetPropertyValue and RemovePropertyValue
-nsresult
-nsCSSFontFaceStyleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID,
-                                         nsAString & aResult) const
-{
-  NS_ENSURE_ARG_RANGE(aFontDescID, eCSSFontDesc_UNKNOWN,
-                      eCSSFontDesc_COUNT - 1);
-
-  aResult.Truncate();
-  if (aFontDescID == eCSSFontDesc_UNKNOWN)
-    return NS_OK;
-
-  const nsCSSValue& val = mDescriptors.Get(aFontDescID);
-
-  if (val.GetUnit() == eCSSUnit_Null) {
-    // Avoid having to check no-value in the Family and Src cases below.
-    return NS_OK;
-  }
-
-  switch (aFontDescID) {
-  case eCSSFontDesc_Family: {
-      // we don't use nsCSSValue::AppendToString here because it doesn't
-      // canonicalize the way we want, and anyway it's overkill when
-      // we know we have eCSSUnit_String
-      NS_ASSERTION(val.GetUnit() == eCSSUnit_String, "unexpected unit");
-      nsDependentString family(val.GetStringBufferValue());
-      nsStyleUtil::AppendEscapedCSSString(family, aResult);
-      return NS_OK;
-    }
-
-  case eCSSFontDesc_Style:
-    val.AppendToString(eCSSProperty_font_style, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Weight:
-    val.AppendToString(eCSSProperty_font_weight, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Stretch:
-    val.AppendToString(eCSSProperty_font_stretch, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_FontFeatureSettings:
-    nsStyleUtil::AppendFontFeatureSettings(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_FontVariationSettings:
-    nsStyleUtil::AppendFontVariationSettings(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_FontLanguageOverride:
-    val.AppendToString(eCSSProperty_font_language_override, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Display:
-    NS_ASSERTION(val.GetUnit() == eCSSUnit_Enumerated,
-                 "unknown unit for font-display descriptor");
-    AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(val.GetIntValue(),
-                                       nsCSSProps::kFontDisplayKTable), aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_Src:
-    nsStyleUtil::AppendSerializedFontSrc(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_UnicodeRange:
-    nsStyleUtil::AppendUnicodeRange(val, aResult);
-    return NS_OK;
-
-  case eCSSFontDesc_UNKNOWN:
-  case eCSSFontDesc_COUNT:
-    ;
-  }
-  NS_NOTREACHED("nsCSSFontFaceStyleDecl::GetPropertyValue: "
-                "out-of-range value got to the switch");
-  return NS_ERROR_INVALID_ARG;
-}
-
-
-void
-nsCSSFontFaceStyleDecl::GetCssText(nsAString & aCssText)
-{
-  GetCssTextImpl(aCssText);
-}
-
-void
-nsCSSFontFaceStyleDecl::GetCssTextImpl(nsAString& aCssText) const
-{
-  nsAutoString descStr;
-
-  aCssText.Truncate();
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1)) {
-    if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null &&
-        NS_SUCCEEDED(GetPropertyValue(id, descStr))) {
-      NS_ASSERTION(descStr.Length() > 0,
-                   "GetCssText: non-null unit, empty property value");
-      aCssText.AppendLiteral("  ");
-      aCssText.AppendASCII(nsCSSProps::GetStringValue(id).get());
-      aCssText.AppendLiteral(": ");
-      aCssText.Append(descStr);
-      aCssText.AppendLiteral(";\n");
-    }
-  }
-}
-
-void
-nsCSSFontFaceStyleDecl::SetCssText(const nsAString& aCssText,
-                                   nsIPrincipal* aSubjectPrincipal,
-                                   ErrorResult& aRv)
-{
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); // bug 443978
-}
-
-NS_IMETHODIMP
-nsCSSFontFaceStyleDecl::GetPropertyValue(const nsAString & propertyName,
-                                         nsAString & aResult)
-{
-  return GetPropertyValue(nsCSSProps::LookupFontDesc(propertyName), aResult);
-}
-
-already_AddRefed<dom::CSSValue>
-nsCSSFontFaceStyleDecl::GetPropertyCSSValue(const nsAString & propertyName,
-                                            ErrorResult& aRv)
-{
-  // ??? nsDOMCSSDeclaration returns null/NS_OK, but that seems wrong.
-  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
-  return nullptr;
-}
-
-NS_IMETHODIMP
-nsCSSFontFaceStyleDecl::RemoveProperty(const nsAString & propertyName,
-                                       nsAString & aResult)
-{
-  nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(propertyName);
-  NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN &&
-               descID < eCSSFontDesc_COUNT,
-               "LookupFontDesc returned value out of range");
-
-  if (descID == eCSSFontDesc_UNKNOWN) {
-    aResult.Truncate();
-  } else {
-    nsresult rv = GetPropertyValue(descID, aResult);
-    NS_ENSURE_SUCCESS(rv, rv);
-    mDescriptors.Get(descID).Reset();
-  }
-  return NS_OK;
-}
-
-void
-nsCSSFontFaceStyleDecl::GetPropertyPriority(const nsAString & propertyName,
-                                            nsAString & aResult)
-{
-  // font descriptors do not have priorities at present
-  aResult.Truncate();
-}
-
-NS_IMETHODIMP
-nsCSSFontFaceStyleDecl::SetProperty(const nsAString& propertyName,
-                                    const nsAString& value,
-                                    const nsAString& priority,
-                                    nsIPrincipal* aSubjectPrincipal)
-{
-  // FIXME(heycam): If we are changing unicode-range, then a FontFace object
-  // representing this rule must have its mUnicodeRange value invalidated.
-
-  return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
-}
-
-uint32_t
-nsCSSFontFaceStyleDecl::Length()
-{
-  uint32_t len = 0;
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1)) {
-    if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
-      len++;
-    }
-  }
-
-  return len;
-}
-
-void
-nsCSSFontFaceStyleDecl::IndexedGetter(uint32_t index, bool& aFound, nsAString & aResult)
-{
-  int32_t nset = -1;
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1)) {
-    if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
-      nset++;
-      if (nset == int32_t(index)) {
-        aFound = true;
-        aResult.AssignASCII(nsCSSProps::GetStringValue(id).get());
-        return;
-      }
-    }
-  }
-  aFound = false;
-}
-
-css::Rule*
-nsCSSFontFaceStyleDecl::GetParentRule()
-{
-  return ContainingRule();
-}
-
-nsINode*
-nsCSSFontFaceStyleDecl::GetParentObject()
-{
-  return ContainingRule()->GetDocument();
-}
-
-JSObject*
-nsCSSFontFaceStyleDecl::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
-{
-  return mozilla::dom::CSSStyleDeclarationBinding::Wrap(cx, this, aGivenProto);
-}
-
-// -------------------------------------------
-// nsCSSFontFaceRule
-//
-
-/* virtual */ already_AddRefed<css::Rule>
-nsCSSFontFaceRule::Clone() const
-{
-  RefPtr<css::Rule> clone = new nsCSSFontFaceRule(*this);
-  return clone.forget();
-}
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSFontFaceRule)
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsCSSFontFaceRule,
-                                               mozilla::css::Rule)
-  // Keep this in sync with IsCCLeaf.
-
-  // Trace the wrapper for our declaration.  This just expands out
-  // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
-  // directly because the wrapper is on the declaration, not on us.
-  tmp->mDecl.TraceWrapper(aCallbacks, aClosure);
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsCSSFontFaceRule,
-                                                mozilla::css::Rule)
-  // Keep this in sync with IsCCLeaf.
-
-  // Unlink the wrapper for our declaraton.  This just expands out
-  // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
-  // directly because the wrapper is on the declaration, not on us.
-  tmp->mDecl.ReleaseWrapper(static_cast<nsISupports*>(p));
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsCSSFontFaceRule,
-                                                  mozilla::css::Rule)
-  // Keep this in sync with IsCCLeaf.
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-bool
-nsCSSFontFaceRule::IsCCLeaf() const
-{
-  if (!Rule::IsCCLeaf()) {
-    return false;
-  }
-
-  return !mDecl.PreservingWrapper();
-}
-
-NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(nsCSSFontFaceRule, mozilla::css::Rule)
-
-#ifdef DEBUG
-void
-nsCSSFontFaceRule::List(FILE* out, int32_t aIndent) const
-{
-  nsCString baseInd, descInd;
-  for (int32_t indent = aIndent; --indent >= 0; ) {
-    baseInd.AppendLiteral("  ");
-    descInd.AppendLiteral("  ");
-  }
-  descInd.AppendLiteral("  ");
-
-  nsString descStr;
-
-  fprintf_stderr(out, "%s@font-face {\n", baseInd.get());
-  for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
-       id < eCSSFontDesc_COUNT;
-       id = nsCSSFontDesc(id + 1))
-    if (mDecl.mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
-      if (NS_FAILED(mDecl.GetPropertyValue(id, descStr)))
-        descStr.AssignLiteral("#<serialization error>");
-      else if (descStr.Length() == 0)
-        descStr.AssignLiteral("#<serialization missing>");
-      fprintf_stderr(out, "%s%s: %s\n",
-                     descInd.get(), nsCSSProps::GetStringValue(id).get(),
-                     NS_ConvertUTF16toUTF8(descStr).get());
-    }
-  fprintf_stderr(out, "%s}\n", baseInd.get());
-}
-#endif
-
-uint16_t
-nsCSSFontFaceRule::Type() const
-{
-  return CSSRuleBinding::FONT_FACE_RULE;
-}
-
-void
-nsCSSFontFaceRule::GetCssText(nsAString& aCssText) const
-{
-  nsAutoString propText;
-  mDecl.GetCssTextImpl(propText);
-
-  aCssText.AssignLiteral("@font-face {\n");
-  aCssText.Append(propText);
-  aCssText.Append('}');
-}
-
-nsICSSDeclaration*
-nsCSSFontFaceRule::Style()
-{
-  return &mDecl;
-}
-
-// Arguably these should forward to nsCSSFontFaceStyleDecl methods.
-void
-nsCSSFontFaceRule::SetDesc(nsCSSFontDesc aDescID, nsCSSValue const & aValue)
-{
-  NS_PRECONDITION(aDescID > eCSSFontDesc_UNKNOWN &&
-                  aDescID < eCSSFontDesc_COUNT,
-                  "aDescID out of range in nsCSSFontFaceRule::SetDesc");
-
-  // FIXME: handle dynamic changes
-
-  // FIXME(heycam): If we are changing unicode-range, then a FontFace object
-  // representing this rule must have its mUnicodeRange value invalidated.
-
-  mDecl.mDescriptors.Get(aDescID) = aValue;
-}
-
-void
-nsCSSFontFaceRule::GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue)
-{
-  NS_PRECONDITION(aDescID > eCSSFontDesc_UNKNOWN &&
-                  aDescID < eCSSFontDesc_COUNT,
-                  "aDescID out of range in nsCSSFontFaceRule::GetDesc");
-
-  aValue = mDecl.mDescriptors.Get(aDescID);
-}
-
-/* virtual */ size_t
-nsCSSFontFaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-{
-  return aMallocSizeOf(this);
-
-  // Measurement of the following members may be added later if DMD finds it is
-  // worthwhile:
-  // - mDecl
-}
-
-/* virtual */ JSObject*
-nsCSSFontFaceRule::WrapObject(JSContext* aCx,
-                              JS::Handle<JSObject*> aGivenProto)
-{
-  return CSSFontFaceRuleBinding::Wrap(aCx, this, aGivenProto);
-}
deleted file mode 100644
--- a/layout/style/nsCSSFontFaceRule.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/* -*- 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/. */
-
-#ifndef nsCSSFontFaceRule_h
-#define nsCSSFontFaceRule_h
-
-#include "mozilla/css/Rule.h"
-#include "nsCSSValue.h"
-#include "nsICSSDeclaration.h"
-
-namespace mozilla {
-
-namespace dom {
-class DocGroup;
-} // namespace dom
-
-struct CSSFontFaceDescriptors
-{
-#define CSS_FONT_DESC(name_, method_) nsCSSValue m##method_;
-#include "nsCSSFontDescList.h"
-#undef CSS_FONT_DESC
-
-  const nsCSSValue& Get(nsCSSFontDesc aFontDescID) const;
-  nsCSSValue& Get(nsCSSFontDesc aFontDescID);
-
-private:
-  static nsCSSValue CSSFontFaceDescriptors::* const Fields[];
-};
-
-} // namespace mozilla
-
-// A nsCSSFontFaceStyleDecl is always embedded in a nsCSSFontFaceRule.
-class nsCSSFontFaceRule;
-class nsCSSFontFaceStyleDecl final : public nsICSSDeclaration
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
-  virtual already_AddRefed<mozilla::dom::CSSValue>
-  GetPropertyCSSValue(const nsAString& aProp, mozilla::ErrorResult& aRv)
-    override;
-  using nsICSSDeclaration::GetPropertyCSSValue;
-
-  virtual nsINode *GetParentObject() override;
-  virtual void IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) override;
-
-  nsresult GetPropertyValue(nsCSSFontDesc aFontDescID,
-                            nsAString & aResult) const;
-
-  virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
-
-protected:
-  ~nsCSSFontFaceStyleDecl() {}
-
-  friend class nsCSSFontFaceRule;
-
-  inline nsCSSFontFaceRule* ContainingRule();
-  inline const nsCSSFontFaceRule* ContainingRule() const;
-
-  mozilla::CSSFontFaceDescriptors mDescriptors;
-
-  // The actual implementation of GetCssText, so we can make it const.
-  // We can't make nsICSSDeclaration::GetCssText const, because some
-  // subclasses call non-const methods in their implementations.
-  void GetCssTextImpl(nsAString& aCssText) const;
-
-private:
-  // NOT TO BE IMPLEMENTED
-  // This object cannot be allocated on its own, only as part of
-  // nsCSSFontFaceRule.
-  void* operator new(size_t size) CPP_THROW_NEW;
-};
-
-class nsCSSFontFaceRule final : public mozilla::css::Rule
-{
-public:
-  nsCSSFontFaceRule(uint32_t aLineNumber, uint32_t aColumnNumber)
-    : mozilla::css::Rule(aLineNumber, aColumnNumber)
-  {
-  }
-
-  nsCSSFontFaceRule(const nsCSSFontFaceRule& aCopy)
-    // copy everything except our reference count
-    : mozilla::css::Rule(aCopy), mDecl(aCopy.mDecl)
-  {
-  }
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsCSSFontFaceRule,
-                                                         mozilla::css::Rule)
-  virtual bool IsCCLeaf() const override;
-
-#ifdef DEBUG
-  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
-#endif
-  virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
-
-  void SetDesc(nsCSSFontDesc aDescID, nsCSSValue const & aValue);
-  void GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue);
-
-  // WebIDL interface
-  uint16_t Type() const override;
-  void GetCssText(nsAString& aCssText) const override;
-  nsICSSDeclaration* Style();
-
-  virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
-
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override;
-
-  void GetDescriptors(mozilla::CSSFontFaceDescriptors& aDescriptors) const
-    { aDescriptors = mDecl.mDescriptors; }
-
-protected:
-  ~nsCSSFontFaceRule() {}
-
-  friend class nsCSSFontFaceStyleDecl;
-  nsCSSFontFaceStyleDecl mDecl;
-};
-
-// nsFontFaceRuleContainer - used for associating sheet type with
-// specific @font-face rules
-struct nsFontFaceRuleContainer {
-  RefPtr<nsCSSFontFaceRule> mRule;
-  mozilla::SheetType mSheetType;
-};
-
-inline nsCSSFontFaceRule*
-nsCSSFontFaceStyleDecl::ContainingRule()
-{
-  return reinterpret_cast<nsCSSFontFaceRule*>
-    (reinterpret_cast<char*>(this) - offsetof(nsCSSFontFaceRule, mDecl));
-}
-
-inline const nsCSSFontFaceRule*
-nsCSSFontFaceStyleDecl::ContainingRule() const
-{
-  return reinterpret_cast<const nsCSSFontFaceRule*>
-    (reinterpret_cast<const char*>(this) - offsetof(nsCSSFontFaceRule, mDecl));
-}
-
-#endif // nsCSSFontFaceRule_h
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -227,16 +227,19 @@ support-files = flexbox_layout_testcases
 [test_font_face_parser.html]
 [test_font_family_parsing.html]
 [test_font_family_serialization.html]
 skip-if = !stylo
 [test_font_loading_api.html]
 support-files =
   BitPattern.woff
   file_font_loading_api_vframe.html
+# This test checks font loading state. When loaded second time, fonts may be
+# loaded synchronously, causing this test to fail in test-verify task.
+skip-if = verify
 [test_garbage_at_end_of_declarations.html]
 [test_grid_container_shorthands.html]
 [test_grid_item_shorthands.html]
 [test_grid_shorthand_serialization.html]
 [test_grid_computed_values.html]
 [test_group_insertRule.html]
 [test_hover_quirk.html]
 [test_html_attribute_computed_values.html]
--- a/layout/style/test/test_font_face_parser.html
+++ b/layout/style/test/test_font_face_parser.html
@@ -27,24 +27,24 @@ function runTest() {
     { rule: "@font-face { }",   d: {} },
     { rule: "@font-face {",     d: {} },
     { rule: "@font-face { ; }", d: {}, noncanonical: true },
 
     // Correct font-family.
     { rule: _("font-family: \"Mouse\";"), d: {"font-family" : "\"Mouse\""} },
     { rule: _("font-family: \"Mouse\""),  d: {"font-family" : "\"Mouse\""},
       noncanonical: true },
-    { rule: _("font-family: Mouse;"),  d: {"font-family" : "\"Mouse\"" },
+    { rule: _("font-family: Mouse;"),  d: {"font-family" : "Mouse" },
       noncanonical: true },
-    { rule: _("font-family: Mouse"),  d: {"font-family" : "\"Mouse\"" },
+    { rule: _("font-family: Mouse"),  d: {"font-family" : "Mouse" },
       noncanonical: true },
 
     // Correct but unusual font-family.
     { rule: _("font-family: Hoefler Text;"),
-      d: {"font-family" : "\"Hoefler Text\""},
+      d: {"font-family" : "Hoefler Text"},
       noncanonical: true },
 
     // Incorrect font-family.
     { rule: _("font-family:"),        d: {} },
     { rule: _("font-family \"Mouse\""), d: {} },
     { rule: _("font-family: *"),      d: {} },
     { rule: _("font-family: Mouse, Rat"), d: {} },
     { rule: _("font-family: sans-serif"), d: {} },
@@ -105,28 +105,28 @@ function runTest() {
       d: { "src" : "url(\"/fonts/Mouse\") format(\"truetype\")" } },
     { rule: _("src: url(\"/fonts/Mouse\") format(\"truetype\", \"opentype\");"),
       d: { "src" : "url(\"/fonts/Mouse\") format(\"truetype\", \"opentype\")" } },
 
     { rule: _("src: url(\"/fonts/Mouse\"), url(\"/fonts/Rat\");"),
       d: { "src" : "url(\"/fonts/Mouse\"), url(\"/fonts/Rat\")" } },
 
     { rule: _("src: local(Mouse), url(\"/fonts/Mouse\");"),
-      d: { "src" : "local(\"Mouse\"), url(\"/fonts/Mouse\")" },
+      d: { "src" : "local(Mouse), url(\"/fonts/Mouse\")" },
       noncanonical: true },
 
     { rule: _("src: local(\"老鼠\"), url(\"/fonts/Mouse\");"),
       d: { "src" : "local(\"老鼠\"), url(\"/fonts/Mouse\")" } },
 
     { rule: _("src: local(\"老鼠\"), url(\"/fonts/Mouse\") format(\"truetype\");"),
       d: { "src" : "local(\"老鼠\"), url(\"/fonts/Mouse\") format(\"truetype\")" } },
 
     // Correct but unusual src:
     { rule: _("src: local(Hoefler Text);"),
-      d: {"src" : "local(\"Hoefler Text\")"}, noncanonical: true },
+      d: {"src" : "local(Hoefler Text)"}, noncanonical: true },
 
     // Incorrect src:
     { rule: _("src:"), d: {} },
     { rule: _("src: \"/fonts/Mouse\";"), d: {} },
     { rule: _("src: /fonts/Mouse;"), d: {} },
     { rule: _("src: url(\"/fonts/Mouse\") format(truetype);"), d: {} },
     { rule: _("src: url(\"/fonts/Mouse\") format(\"truetype\",opentype);"), d: {} },
     { rule: _("src: local(*);"), d: {} },
@@ -138,25 +138,25 @@ function runTest() {
     // Repeated descriptors
     { rule: _("font-weight: 700; font-weight: 200;"),
       d: {"font-weight" : "200"},
       noncanonical: true },
     { rule: _("src: url(\"/fonts/Cat\"); src: url(\"/fonts/Mouse\");"),
       d: { "src" : "url(\"/fonts/Mouse\")" },
       noncanonical: true },
     { rule: _("src: local(Cat); src: local(Mouse)"),
-      d: { "src" : "local(\"Mouse\")" },
+      d: { "src" : "local(Mouse)" },
       noncanonical: true },
 
     // Correct parenthesis matching for local()
     { rule: _("src: local(Mouse); src: local(Cat(); src: local(Rat); )"),
-      d: { "src" : "local(\"Mouse\")" },
+      d: { "src" : "local(Mouse)" },
       noncanonical: true },
     { rule: _("src: local(Mouse); src: local(\"Cat\"; src: local(Rat); )"),
-      d: { "src" : "local(\"Mouse\")" },
+      d: { "src" : "local(Mouse)" },
       noncanonical: true },
 
     // Correct parenthesis matching for format()
     { rule: _("src: url(\"/fonts/Mouse\"); " +
 	      "src: url(\"/fonts/Cat\") format(Cat(); src: local(Rat); )"),
       d: { "src" : "url(\"/fonts/Mouse\")" },
       noncanonical: true },
     { rule: _("src: url(\"/fonts/Mouse\"); " +
--- a/layout/style/test/test_font_loading_api.html
+++ b/layout/style/test/test_font_loading_api.html
@@ -56,17 +56,17 @@ var invalidValues = {
   unicodeRange: "U+1????-2????",
   variant: "inherit",
   featureSettings: "dlig",
   display: "normal"
 };
 
 // Invalid font family names.
 var invalidFontFamilyNames = [
-  "", "\"\"", "sans-serif", "A, B", "inherit", "a 1"
+  "", "sans-serif", "A, B", "inherit", "a 1"
 ];
 
 // Font family list where at least one is likely to be available on
 // platforms we care about.
 var likelyPlatformFonts = "Helvetica Neue, Bitstream Vera Sans, Bitstream Vera Sans Roman, FreeSans, Free Sans, SwissA, DejaVu Sans, Arial";
 
 // Will hold an ArrayBuffer containing a valid font.
 var fontData;
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -8,17 +8,16 @@
 
 #![deny(missing_docs)]
 
 #[cfg(feature = "gecko")]
 use computed_values::{font_stretch, font_style, font_weight};
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{SourceLocation, CowRcStr};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
-#[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
 #[cfg(feature = "gecko")] use cssparser::UnicodeRange;
 use parser::{ParserContext, ParserErrorContext, Parse};
 #[cfg(feature = "gecko")]
 use properties::longhands::font_language_override;
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
@@ -266,51 +265,36 @@ macro_rules! font_face_descriptors_commo
                 #[$doc]
                 pub $ident: Option<$ty>,
             )*
             /// Line and column of the @font-face rule source code.
             pub source_location: SourceLocation,
         }
 
         impl FontFaceRuleData {
-            fn empty(location: SourceLocation) -> Self {
+            /// Create an empty font-face rule
+            pub fn empty(location: SourceLocation) -> Self {
                 FontFaceRuleData {
                     $(
                         $ident: None,
                     )*
                     source_location: location,
                 }
             }
 
-            /// Convert to Gecko types
-            #[cfg(feature = "gecko")]
-            pub fn set_descriptors(self, descriptors: &mut CSSFontFaceDescriptors) {
-                $(
-                    if let Some(value) = self.$ident {
-                        descriptors.$gecko_ident.set_from(value)
-                    }
-                )*
-                // Leave unset descriptors to eCSSUnit_Null,
-                // FontFaceSet::FindOrCreateUserFontEntryFromFontFace does the defaulting
-                // to initial values.
-            }
-        }
-
-        impl ToCssWithGuard for FontFaceRuleData {
-            // Serialization of FontFaceRule is not specced.
-            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
-                dest.write_str("@font-face {\n")?;
+            /// Serialization of declarations in the FontFaceRule
+            pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
                 $(
                     if let Some(ref value) = self.$ident {
                         dest.write_str(concat!("  ", $name, ": "))?;
                         ToCss::to_css(value, &mut CssWriter::new(dest))?;
                         dest.write_str(";\n")?;
                     }
                 )*
-                dest.write_str("}")
+                Ok(())
             }
         }
 
        impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
            type Declaration = ();
            type Error = StyleParseErrorKind<'i>;
 
            fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
@@ -329,16 +313,25 @@ macro_rules! font_face_descriptors_commo
                     _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 Ok(())
             }
         }
     }
 }
 
+impl ToCssWithGuard for FontFaceRuleData {
+    // Serialization of FontFaceRule is not specced.
+    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
+        dest.write_str("@font-face {\n")?;
+        self.decl_to_css(dest)?;
+        dest.write_str("}")
+    }
+}
+
 macro_rules! font_face_descriptors {
     (
         mandatory descriptors = [
             $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
         ]
         optional descriptors = [
             $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )*
         ]
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -3,33 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! This file lists all arc FFI types and defines corresponding addref
 //! and release functions. This list corresponds to ServoArcTypeList.h
 //! file in Gecko.
 
 #![allow(non_snake_case, missing_docs)]
 
-use gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoImportRule, RawServoSupportsRule};
-use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule};
+use gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoImportRule};
+use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule, RawServoSupportsRule};
 use gecko_bindings::bindings::{RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule};
 use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule};
 use gecko_bindings::bindings::ServoCssRules;
-use gecko_bindings::structs::{RawServoAnimationValue, RawServoDeclarationBlock, RawServoStyleRule};
-use gecko_bindings::structs::{RawServoMediaList, RawServoStyleSheetContents};
+use gecko_bindings::structs::{RawServoAnimationValue, RawServoDeclarationBlock, RawServoFontFaceRule};
+use gecko_bindings::structs::{RawServoMediaList, RawServoStyleRule, RawServoStyleSheetContents};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
 use media_queries::MediaList;
 use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use rule_tree::StrongRuleNode;
 use servo_arc::{Arc, ArcBorrow};
 use shared_lock::Locked;
 use std::{mem, ptr};
 use stylesheets::{CssRules, StylesheetContents, StyleRule, ImportRule, KeyframesRule, MediaRule};
-use stylesheets::{FontFeatureValuesRule, NamespaceRule, PageRule, SupportsRule, DocumentRule};
+use stylesheets::{FontFaceRule, FontFeatureValuesRule, NamespaceRule, PageRule, SupportsRule, DocumentRule};
 use stylesheets::keyframes_rule::Keyframe;
 
 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 {}
@@ -86,16 +86,19 @@ impl_arc_ffi!(Locked<SupportsRule> => Ra
               [Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]);
 
 impl_arc_ffi!(Locked<DocumentRule> => RawServoDocumentRule
               [Servo_DocumentRule_AddRef, Servo_DocumentRule_Release]);
 
 impl_arc_ffi!(Locked<FontFeatureValuesRule> => RawServoFontFeatureValuesRule
               [Servo_FontFeatureValuesRule_AddRef, Servo_FontFeatureValuesRule_Release]);
 
+impl_arc_ffi!(Locked<FontFaceRule> => RawServoFontFaceRule
+              [Servo_FontFaceRule_AddRef, Servo_FontFaceRule_Release]);
+
 // RuleNode is a Arc-like type but it does not use Arc.
 
 impl StrongRuleNode {
     pub fn into_strong(self) -> RawServoRuleNodeStrong {
         let ptr = self.ptr();
         mem::forget(self);
         unsafe { mem::transmute(ptr) }
     }
--- a/servo/components/style/gecko/generated/bindings.rs
+++ b/servo/components/style/gecko/generated/bindings.rs
@@ -33,16 +33,17 @@ use gecko_bindings::structs::RawGeckoDoc
 use gecko_bindings::structs::RawGeckoElement;
 use gecko_bindings::structs::Element;
 use gecko_bindings::structs::RawGeckoKeyframeList;
 use gecko_bindings::structs::RawGeckoPropertyValuePairList;
 use gecko_bindings::structs::RawGeckoComputedKeyframeValuesList;
 use gecko_bindings::structs::RawGeckoFontFaceRuleList;
 use gecko_bindings::structs::RawGeckoNode;
 use gecko_bindings::structs::RawServoAnimationValue;
+use gecko_bindings::structs::RawServoFontFaceRule;
 use gecko_bindings::structs::RawGeckoServoAnimationValueList;
 use gecko_bindings::structs::RawServoMediaList;
 use gecko_bindings::structs::RawServoStyleSheetContents;
 use gecko_bindings::structs::RawServoDeclarationBlock;
 use gecko_bindings::structs::RawServoStyleRule;
 use gecko_bindings::structs::RawGeckoPresContext;
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use gecko_bindings::structs::RawGeckoStyleAnimationList;
@@ -72,17 +73,16 @@ use gecko_bindings::structs::ServoStyleS
 use gecko_bindings::structs::SheetParsingMode;
 use gecko_bindings::structs::StyleBasicShapeType;
 use gecko_bindings::structs::StyleShapeSource;
 use gecko_bindings::structs::StyleTransition;
 use gecko_bindings::structs::gfxFontFeatureValueSet;
 use gecko_bindings::structs::nsCSSCounterDesc;
 use gecko_bindings::structs::nsCSSCounterStyleRule;
 use gecko_bindings::structs::nsCSSFontDesc;
-use gecko_bindings::structs::nsCSSFontFaceRule;
 use gecko_bindings::structs::nsCSSKeyword;
 use gecko_bindings::structs::nsCSSPropertyID;
 use gecko_bindings::structs::nsCSSPropertyIDSet;
 use gecko_bindings::structs::nsCSSRect;
 use gecko_bindings::structs::nsCSSShadowArray;
 use gecko_bindings::structs::nsCSSUnit;
 use gecko_bindings::structs::nsCSSValue;
 use gecko_bindings::structs::nsCSSValueSharedList;
@@ -425,16 +425,19 @@ pub type RawServoFontFeatureValuesRuleBo
 pub type RawServoFontFeatureValuesRuleBorrowedOrNull<'a> = Option<&'a RawServoFontFeatureValuesRule>;
 enum RawServoFontFeatureValuesRuleVoid { }
 pub struct RawServoFontFeatureValuesRule(RawServoFontFeatureValuesRuleVoid);
 pub type RawServoRuleNodeStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoRuleNode>;
 pub type RawServoRuleNodeBorrowed<'a> = &'a RawServoRuleNode;
 pub type RawServoRuleNodeBorrowedOrNull<'a> = Option<&'a RawServoRuleNode>;
 enum RawServoRuleNodeVoid { }
 pub struct RawServoRuleNode(RawServoRuleNodeVoid);
+pub type RawServoFontFaceRuleStrong = ::gecko_bindings::sugar::ownership::Strong<RawServoFontFaceRule>;
+pub type RawServoFontFaceRuleBorrowed<'a> = &'a RawServoFontFaceRule;
+pub type RawServoFontFaceRuleBorrowedOrNull<'a> = Option<&'a RawServoFontFaceRule>;
 
 extern "C" {
     pub fn Gecko_EnsureTArrayCapacity(
         aArray: *mut ::std::os::raw::c_void,
         aCapacity: usize,
         aElementSize: usize,
     );
 }
@@ -537,16 +540,22 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_RuleNode_AddRef(ptr: RawServoRuleNodeBorrowed);
 }
 extern "C" {
     pub fn Servo_RuleNode_Release(ptr: RawServoRuleNodeBorrowed);
 }
 extern "C" {
+    pub fn Servo_FontFaceRule_AddRef(ptr: RawServoFontFaceRuleBorrowed);
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Release(ptr: RawServoFontFaceRuleBorrowed);
+}
+extern "C" {
     pub fn Servo_StyleSet_Drop(ptr: RawServoStyleSetOwned);
 }
 extern "C" {
     pub fn Servo_AuthorStyles_Drop(ptr: RawServoAuthorStylesOwned);
 }
 extern "C" {
     pub fn Servo_SelectorList_Drop(ptr: RawServoSelectorListOwned);
 }
@@ -1559,31 +1568,16 @@ extern "C" {
 }
 extern "C" {
     pub fn Gecko_CSSKeywordString(
         keyword: nsCSSKeyword,
         len: *mut u32,
     ) -> *const ::std::os::raw::c_char;
 }
 extern "C" {
-    pub fn Gecko_CSSFontFaceRule_Create(line: u32, column: u32) -> *mut nsCSSFontFaceRule;
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_Clone(rule: *const nsCSSFontFaceRule) -> *mut nsCSSFontFaceRule;
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_GetCssText(rule: *const nsCSSFontFaceRule, result: *mut nsAString);
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_AddRef(aPtr: *mut nsCSSFontFaceRule);
-}
-extern "C" {
-    pub fn Gecko_CSSFontFaceRule_Release(aPtr: *mut nsCSSFontFaceRule);
-}
-extern "C" {
     pub fn Gecko_CSSCounterStyle_Create(name: *mut nsAtom) -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
     pub fn Gecko_CSSCounterStyle_Clone(
         rule: *const nsCSSCounterStyleRule,
     ) -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
@@ -2457,17 +2451,28 @@ extern "C" {
         rule: RawServoFontFeatureValuesRuleBorrowed,
         result: *mut nsAString,
     );
 }
 extern "C" {
     pub fn Servo_CssRules_GetFontFaceRuleAt(
         rules: ServoCssRulesBorrowed,
         index: u32,
-    ) -> *mut nsCSSFontFaceRule;
+        line: *mut u32,
+        column: *mut u32,
+    ) -> RawServoFontFaceRuleStrong;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Debug(rule: RawServoFontFaceRuleBorrowed, result: *mut nsACString);
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetCssText(
+        rule: RawServoFontFaceRuleBorrowed,
+        result: *mut nsAString,
+    );
 }
 extern "C" {
     pub fn Servo_CssRules_GetCounterStyleRuleAt(
         rules: ServoCssRulesBorrowed,
         index: u32,
     ) -> *mut nsCSSCounterStyleRule;
 }
 extern "C" {
@@ -2608,16 +2613,74 @@ extern "C" {
 }
 extern "C" {
     pub fn Servo_FontFeatureValuesRule_GetValueText(
         rule: RawServoFontFeatureValuesRuleBorrowed,
         result: *mut nsAString,
     );
 }
 extern "C" {
+    pub fn Servo_FontFaceRule_CreateEmpty() -> RawServoFontFaceRuleStrong;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Clone(
+        rule: RawServoFontFaceRuleBorrowed,
+    ) -> RawServoFontFaceRuleStrong;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetSourceLocation(
+        rule: RawServoFontFaceRuleBorrowed,
+        line: *mut u32,
+        column: *mut u32,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_Length(rule: RawServoFontFaceRuleBorrowed) -> u32;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_IndexGetter(
+        rule: RawServoFontFaceRuleBorrowed,
+        index: u32,
+    ) -> nsCSSFontDesc;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetDeclCssText(
+        rule: RawServoFontFaceRuleBorrowed,
+        result: *mut nsAString,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetDescriptor(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+        result: *mut nsCSSValue,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_GetDescriptorCssText(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+        result: *mut nsAString,
+    );
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_SetDescriptor(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+        value: *const nsACString,
+        data: *mut RawGeckoURLExtraData,
+    ) -> bool;
+}
+extern "C" {
+    pub fn Servo_FontFaceRule_ResetDescriptor(
+        rule: RawServoFontFaceRuleBorrowed,
+        desc: nsCSSFontDesc,
+    );
+}
+extern "C" {
     pub fn Servo_ParseProperty(
         property: nsCSSPropertyID,
         value: *const nsACString,
         data: *mut RawGeckoURLExtraData,
         parsing_mode: ParsingMode,
         quirks_mode: nsCompatibility,
         loader: *mut Loader,
     ) -> RawServoDeclarationBlockStrong;
@@ -3261,24 +3324,16 @@ extern "C" {
     pub fn Servo_ParseCounterStyleDescriptor(
         aDescriptor: nsCSSCounterDesc,
         aValue: *const nsACString,
         aURLExtraData: *mut RawGeckoURLExtraData,
         aResult: *mut nsCSSValue,
     ) -> bool;
 }
 extern "C" {
-    pub fn Servo_ParseFontDescriptor(
-        desc_id: nsCSSFontDesc,
-        value: *const nsAString,
-        data: *mut RawGeckoURLExtraData,
-        arg1: nsCSSValueBorrowedMut,
-    ) -> bool;
-}
-extern "C" {
     pub fn Servo_ParseFontShorthandForMatching(
         value: *const nsAString,
         data: *mut RawGeckoURLExtraData,
         family: *mut RefPtr<SharedFontList>,
         style: nsCSSValueBorrowedMut,
         stretch: nsCSSValueBorrowedMut,
         weight: nsCSSValueBorrowedMut,
     ) -> bool;
--- a/servo/components/style/gecko/generated/structs.rs
+++ b/servo/components/style/gecko/generated/structs.rs
@@ -13373,171 +13373,16 @@ pub mod root {
                 )
             );
         }
         impl Clone for ServoTraversalStatistics {
             fn clone(&self) -> Self {
                 *self
             }
         }
-        #[repr(C)]
-        #[derive(Debug)]
-        pub struct CSSFontFaceDescriptors {
-            pub mFamily: root::nsCSSValue,
-            pub mStyle: root::nsCSSValue,
-            pub mWeight: root::nsCSSValue,
-            pub mStretch: root::nsCSSValue,
-            pub mSrc: root::nsCSSValue,
-            pub mUnicodeRange: root::nsCSSValue,
-            pub mFontFeatureSettings: root::nsCSSValue,
-            pub mFontVariationSettings: root::nsCSSValue,
-            pub mFontLanguageOverride: root::nsCSSValue,
-            pub mDisplay: root::nsCSSValue,
-        }
-        extern "C" {
-            #[link_name = "\u{1}_ZN7mozilla22CSSFontFaceDescriptors6FieldsE"]
-            pub static mut CSSFontFaceDescriptors_Fields: [*const root::nsCSSValue; 0usize];
-        }
-        #[test]
-        fn bindgen_test_layout_CSSFontFaceDescriptors() {
-            assert_eq!(
-                ::std::mem::size_of::<CSSFontFaceDescriptors>(),
-                160usize,
-                concat!("Size of: ", stringify!(CSSFontFaceDescriptors))
-            );
-            assert_eq!(
-                ::std::mem::align_of::<CSSFontFaceDescriptors>(),
-                8usize,
-                concat!("Alignment of ", stringify!(CSSFontFaceDescriptors))
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFamily as *const _ as usize
-                },
-                0usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFamily)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mStyle as *const _ as usize
-                },
-                16usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mStyle)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mWeight as *const _ as usize
-                },
-                32usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mWeight)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mStretch as *const _ as usize
-                },
-                48usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mStretch)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mSrc as *const _ as usize
-                },
-                64usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mSrc)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mUnicodeRange as *const _
-                        as usize
-                },
-                80usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mUnicodeRange)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFontFeatureSettings
-                        as *const _ as usize
-                },
-                96usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFontFeatureSettings)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFontVariationSettings
-                        as *const _ as usize
-                },
-                112usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFontVariationSettings)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mFontLanguageOverride
-                        as *const _ as usize
-                },
-                128usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mFontLanguageOverride)
-                )
-            );
-            assert_eq!(
-                unsafe {
-                    &(*(::std::ptr::null::<CSSFontFaceDescriptors>())).mDisplay as *const _ as usize
-                },
-                144usize,
-                concat!(
-                    "Offset of field: ",
-                    stringify!(CSSFontFaceDescriptors),
-                    "::",
-                    stringify!(mDisplay)
-                )
-            );
-        }
         pub mod intl {
             #[allow(unused_imports)]
             use self::super::super::super::root;
             #[repr(C)]
             #[derive(Debug)]
             pub struct LineBreaker {
                 pub mRefCnt: root::nsAutoRefCnt,
             }
@@ -23539,16 +23384,26 @@ pub mod root {
     pub struct RawServoMediaList {
         _unused: [u8; 0],
     }
     impl Clone for RawServoMediaList {
         fn clone(&self) -> Self {
             *self
         }
     }
+    #[repr(C)]
+    #[derive(Debug, Copy)]
+    pub struct RawServoFontFaceRule {
+        _unused: [u8; 0],
+    }
+    impl Clone for RawServoFontFaceRule {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
     pub mod nsStyleTransformMatrix {
         #[allow(unused_imports)]
         use self::super::super::root;
         #[repr(u8)]
         #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
         pub enum MatrixTransformOperator {
             Interpolate = 0,
             Accumulate = 1,
@@ -23622,16 +23477,59 @@ pub mod root {
     pub type RawGeckoFontFaceRuleListBorrowedMut = *mut root::RawGeckoFontFaceRuleList;
     pub type RawGeckoAnimationPropertySegmentBorrowed =
         *const root::RawGeckoAnimationPropertySegment;
     pub type RawGeckoComputedTimingBorrowed = *const root::RawGeckoComputedTiming;
     pub type RawGeckoServoStyleRuleListBorrowedMut = *mut root::RawGeckoServoStyleRuleList;
     pub type RawGeckoCSSPropertyIDListBorrowed = *const root::RawGeckoCSSPropertyIDList;
     pub type RawGeckoStyleChildrenIteratorBorrowedMut = *mut root::RawGeckoStyleChildrenIterator;
     #[repr(C)]
+    #[derive(Debug)]
+    pub struct nsFontFaceRuleContainer {
+        pub mRule: root::RefPtr<root::RawServoFontFaceRule>,
+        pub mSheetType: root::mozilla::SheetType,
+    }
+    #[test]
+    fn bindgen_test_layout_nsFontFaceRuleContainer() {
+        assert_eq!(
+            ::std::mem::size_of::<nsFontFaceRuleContainer>(),
+            16usize,
+            concat!("Size of: ", stringify!(nsFontFaceRuleContainer))
+        );
+        assert_eq!(
+            ::std::mem::align_of::<nsFontFaceRuleContainer>(),
+            8usize,
+            concat!("Alignment of ", stringify!(nsFontFaceRuleContainer))
+        );
+        assert_eq!(
+            unsafe {
+                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mRule as *const _ as usize
+            },
+            0usize,
+            concat!(
+                "Offset of field: ",
+                stringify!(nsFontFaceRuleContainer),
+                "::",
+                stringify!(mRule)
+            )
+        );
+        assert_eq!(
+            unsafe {
+                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mSheetType as *const _ as usize
+            },
+            8usize,
+            concat!(
+                "Offset of field: ",
+                stringify!(nsFontFaceRuleContainer),
+                "::",
+                stringify!(mSheetType)
+            )
+        );
+    }
+    #[repr(C)]
     #[derive(Debug, Copy)]
     pub struct nsDOMStyleSheetSetList {
         _unused: [u8; 0],
     }
     impl Clone for nsDOMStyleSheetSetList {
         fn clone(&self) -> Self {
             *self
         }
@@ -39868,150 +39766,16 @@ pub mod root {
                 "Offset of field: ",
                 stringify!(nsCSSCounterStyleRule),
                 "::",
                 stringify!(mGeneration)
             )
         );
     }
     #[repr(C)]
-    #[derive(Debug)]
-    pub struct nsCSSFontFaceStyleDecl {
-        pub _base: root::nsICSSDeclaration,
-        pub mDescriptors: root::mozilla::CSSFontFaceDescriptors,
-    }
-    #[test]
-    fn bindgen_test_layout_nsCSSFontFaceStyleDecl() {
-        assert_eq!(
-            ::std::mem::size_of::<nsCSSFontFaceStyleDecl>(),
-            192usize,
-            concat!("Size of: ", stringify!(nsCSSFontFaceStyleDecl))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsCSSFontFaceStyleDecl>(),
-            8usize,
-            concat!("Alignment of ", stringify!(nsCSSFontFaceStyleDecl))
-        );
-        assert_eq!(
-            unsafe {
-                &(*(::std::ptr::null::<nsCSSFontFaceStyleDecl>())).mDescriptors as *const _ as usize
-            },
-            32usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsCSSFontFaceStyleDecl),
-                "::",
-                stringify!(mDescriptors)
-            )
-        );
-    }
-    #[repr(C)]
-    #[derive(Debug)]
-    pub struct nsCSSFontFaceRule {
-        pub _base: root::mozilla::css::Rule,
-        pub mDecl: root::nsCSSFontFaceStyleDecl,
-    }
-    #[repr(C)]
-    #[derive(Debug, Copy)]
-    pub struct nsCSSFontFaceRule_cycleCollection {
-        pub _base: root::mozilla::css::Rule_cycleCollection,
-    }
-    #[test]
-    fn bindgen_test_layout_nsCSSFontFaceRule_cycleCollection() {
-        assert_eq!(
-            ::std::mem::size_of::<nsCSSFontFaceRule_cycleCollection>(),
-            16usize,
-            concat!("Size of: ", stringify!(nsCSSFontFaceRule_cycleCollection))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsCSSFontFaceRule_cycleCollection>(),
-            8usize,
-            concat!(
-                "Alignment of ",
-                stringify!(nsCSSFontFaceRule_cycleCollection)
-            )
-        );
-    }
-    impl Clone for nsCSSFontFaceRule_cycleCollection {
-        fn clone(&self) -> Self {
-            *self
-        }
-    }
-    extern "C" {
-        #[link_name = "\u{1}_ZN17nsCSSFontFaceRule21_cycleCollectorGlobalE"]
-        pub static mut nsCSSFontFaceRule__cycleCollectorGlobal:
-            root::nsCSSFontFaceRule_cycleCollection;
-    }
-    #[test]
-    fn bindgen_test_layout_nsCSSFontFaceRule() {
-        assert_eq!(
-            ::std::mem::size_of::<nsCSSFontFaceRule>(),
-            256usize,
-            concat!("Size of: ", stringify!(nsCSSFontFaceRule))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsCSSFontFaceRule>(),
-            8usize,
-            concat!("Alignment of ", stringify!(nsCSSFontFaceRule))
-        );
-        assert_eq!(
-            unsafe { &(*(::std::ptr::null::<nsCSSFontFaceRule>())).mDecl as *const _ as usize },
-            64usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsCSSFontFaceRule),
-                "::",
-                stringify!(mDecl)
-            )
-        );
-    }
-    #[repr(C)]
-    #[derive(Debug)]
-    pub struct nsFontFaceRuleContainer {
-        pub mRule: root::RefPtr<root::nsCSSFontFaceRule>,
-        pub mSheetType: root::mozilla::SheetType,
-    }
-    #[test]
-    fn bindgen_test_layout_nsFontFaceRuleContainer() {
-        assert_eq!(
-            ::std::mem::size_of::<nsFontFaceRuleContainer>(),
-            16usize,
-            concat!("Size of: ", stringify!(nsFontFaceRuleContainer))
-        );
-        assert_eq!(
-            ::std::mem::align_of::<nsFontFaceRuleContainer>(),
-            8usize,
-            concat!("Alignment of ", stringify!(nsFontFaceRuleContainer))
-        );
-        assert_eq!(
-            unsafe {
-                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mRule as *const _ as usize
-            },
-            0usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsFontFaceRuleContainer),
-                "::",
-                stringify!(mRule)
-            )
-        );
-        assert_eq!(
-            unsafe {
-                &(*(::std::ptr::null::<nsFontFaceRuleContainer>())).mSheetType as *const _ as usize
-            },
-            8usize,
-            concat!(
-                "Offset of field: ",
-                stringify!(nsFontFaceRuleContainer),
-                "::",
-                stringify!(mSheetType)
-            )
-        );
-    }
-    #[repr(C)]
     #[derive(Debug, Copy)]
     pub struct nsHtml5StringParser {
         _unused: [u8; 0],
     }
     impl Clone for nsHtml5StringParser {
         fn clone(&self) -> Self {
             *self
         }
@@ -47142,16 +46906,35 @@ pub mod root {
             8usize,
             concat!(
                 "Alignment of template specialization: ",
                 stringify!(root::nsTArray<*mut root::nsISupports>)
             )
         );
     }
     #[test]
+    fn __bindgen_test_layout_RefPtr_open0_RawServoFontFaceRule_close0_instantiation() {
+        assert_eq!(
+            ::std::mem::size_of::<root::RefPtr<root::RawServoFontFaceRule>>(),
+            8usize,
+            concat!(
+                "Size of template specialization: ",
+                stringify!(root::RefPtr<root::RawServoFontFaceRule>)
+            )
+        );
+        assert_eq!(
+            ::std::mem::align_of::<root::RefPtr<root::RawServoFontFaceRule>>(),
+            8usize,
+            concat!(
+                "Alignment of template specialization: ",
+                stringify!(root::RefPtr<root::RawServoFontFaceRule>)
+            )
+        );
+    }
+    #[test]
     fn __bindgen_test_layout_RefPtr_open0_RawServoAnimationValue_close0_instantiation_1() {
         assert_eq!(
             ::std::mem::size_of::<root::RefPtr<root::RawServoAnimationValue>>(),
             8usize,
             concat!(
                 "Size of template specialization: ",
                 stringify!(root::RefPtr<root::RawServoAnimationValue>)
             )
@@ -49301,35 +49084,16 @@ pub mod root {
             8usize,
             concat!(
                 "Alignment of template specialization: ",
                 stringify!(root::RefPtr<root::nsAtom>)
             )
         );
     }
     #[test]
-    fn __bindgen_test_layout_RefPtr_open0_nsCSSFontFaceRule_close0_instantiation() {
-        assert_eq!(
-            ::std::mem::size_of::<root::RefPtr<root::nsCSSFontFaceRule>>(),
-            8usize,
-            concat!(
-                "Size of template specialization: ",
-                stringify!(root::RefPtr<root::nsCSSFontFaceRule>)
-            )
-        );
-        assert_eq!(
-            ::std::mem::align_of::<root::RefPtr<root::nsCSSFontFaceRule>>(),
-            8usize,
-            concat!(
-                "Alignment of template specialization: ",
-                stringify!(root::RefPtr<root::nsCSSFontFaceRule>)
-            )
-        );
-    }
-    #[test]
     fn __bindgen_test_layout_nsRefPtrHashKey_open0_nsAtom_close0_instantiation_5() {
         assert_eq!(
             ::std::mem::size_of::<root::nsRefPtrHashKey<root::nsAtom>>(),
             16usize,
             concat!(
                 "Size of template specialization: ",
                 stringify!(root::nsRefPtrHashKey<root::nsAtom>)
             )
--- a/servo/components/style/gecko/rules.rs
+++ b/servo/components/style/gecko/rules.rs
@@ -3,50 +3,47 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Bindings for CSS Rule objects
 
 use byteorder::{BigEndian, WriteBytesExt};
 use computed_values::{font_stretch, font_style, font_weight};
 use counter_style::{self, CounterBound};
 use cssparser::UnicodeRange;
-use font_face::{FontFaceRuleData, Source, FontDisplay, FontWeight};
+use font_face::{Source, FontDisplay, FontWeight};
 use gecko_bindings::bindings;
-use gecko_bindings::structs::{self, nsCSSFontFaceRule, nsCSSValue};
+use gecko_bindings::structs::{self, nsCSSValue};
 use gecko_bindings::structs::{nsCSSCounterDesc, nsCSSCounterStyleRule};
 use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
 use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr};
 use nsstring::nsString;
 use properties::longhands::font_language_override;
 use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard};
 use std::fmt::{self, Write};
 use std::str;
 use str::CssStringWriter;
 use values::computed::font::FamilyName;
 use values::generics::font::FontTag;
 use values::specified::font::{SpecifiedFontVariationSettings, SpecifiedFontFeatureSettings};
 
-/// A @font-face rule
-pub type FontFaceRule = RefPtr<nsCSSFontFaceRule>;
-
-impl ToNsCssValue for FamilyName {
+impl<'a> ToNsCssValue for &'a FamilyName {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_string_from_atom(&self.name)
     }
 }
 
 impl ToNsCssValue for font_weight::T {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_integer(self.0 as i32)
     }
 }
 
-impl ToNsCssValue for FontWeight {
+impl<'a> ToNsCssValue for &'a FontWeight {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        match self {
+        match *self {
             FontWeight::Normal =>
                 nscssvalue.set_enum(structs::NS_STYLE_FONT_WEIGHT_NORMAL as i32),
             FontWeight::Bold =>
                 nscssvalue.set_enum(structs::NS_STYLE_FONT_WEIGHT_BOLD as i32),
             FontWeight::Weight(weight) => nscssvalue.set_integer(weight.0 as i32),
         }
     }
 }
@@ -54,49 +51,49 @@ impl ToNsCssValue for FontWeight {
 impl ToNsCssValue for FontTag {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let mut raw = [0u8; 4];
         (&mut raw[..]).write_u32::<BigEndian>(self.0).unwrap();
         nscssvalue.set_string(str::from_utf8(&raw).unwrap());
     }
 }
 
-impl ToNsCssValue for SpecifiedFontFeatureSettings {
+impl<'a> ToNsCssValue for &'a SpecifiedFontFeatureSettings {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         if self.0.is_empty() {
             nscssvalue.set_normal();
             return;
         }
 
-        nscssvalue.set_pair_list(self.0.into_iter().map(|entry| {
+        nscssvalue.set_pair_list(self.0.iter().map(|entry| {
             let mut index = nsCSSValue::null();
             index.set_integer(entry.value.value());
             (entry.tag.into(), index)
         }))
     }
 }
 
-impl ToNsCssValue for SpecifiedFontVariationSettings {
+impl<'a> ToNsCssValue for &'a SpecifiedFontVariationSettings {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         if self.0.is_empty() {
             nscssvalue.set_normal();
             return;
         }
 
-        nscssvalue.set_pair_list(self.0.into_iter().map(|entry| {
+        nscssvalue.set_pair_list(self.0.iter().map(|entry| {
             let mut value = nsCSSValue::null();
             value.set_number(entry.value.into());
             (entry.tag.into(), value)
         }))
     }
 }
 
-impl ToNsCssValue for font_language_override::SpecifiedValue {
+impl<'a> ToNsCssValue for &'a font_language_override::SpecifiedValue {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        match self {
+        match *self {
             font_language_override::SpecifiedValue::Normal => nscssvalue.set_normal(),
             font_language_override::SpecifiedValue::Override(ref lang) => nscssvalue.set_string(&*lang),
             // This path is unreachable because the descriptor is only specified by the user.
             font_language_override::SpecifiedValue::System(_) => unreachable!(),
         }
     }
 }
 
@@ -104,19 +101,19 @@ macro_rules! map_enum {
     (
         $(
             $prop:ident {
                 $($servo:ident => $gecko:ident,)+
             }
         )+
     ) => {
         $(
-            impl ToNsCssValue for $prop::T {
+            impl<'a> ToNsCssValue for &'a $prop::T {
                 fn convert(self, nscssvalue: &mut nsCSSValue) {
-                    nscssvalue.set_enum(match self {
+                    nscssvalue.set_enum(match *self {
                         $( $prop::T::$servo => structs::$gecko as i32, )+
                     })
                 }
             }
         )+
     }
 }
 
@@ -135,105 +132,71 @@ map_enum! {
         SemiCondensed   => NS_FONT_STRETCH_SEMI_CONDENSED,
         SemiExpanded    => NS_FONT_STRETCH_SEMI_EXPANDED,
         Expanded        => NS_FONT_STRETCH_EXPANDED,
         ExtraExpanded   => NS_FONT_STRETCH_EXTRA_EXPANDED,
         UltraExpanded   => NS_FONT_STRETCH_ULTRA_EXPANDED,
     }
 }
 
-impl ToNsCssValue for Vec<Source> {
+impl<'a> ToNsCssValue for &'a Vec<Source> {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let src_len = self.iter().fold(0, |acc, src| {
             acc + match *src {
                 // Each format hint takes one position in the array of mSrc.
                 Source::Url(ref url) => url.format_hints.len() + 1,
                 Source::Local(_) => 1,
             }
         });
         let mut target_srcs =
             nscssvalue.set_array(src_len as i32).as_mut_slice().iter_mut();
         macro_rules! next { () => {
             target_srcs.next().expect("Length of target_srcs should be enough")
         } }
-        for src in self.into_iter() {
-            match src {
-                Source::Url(url) => {
+        for src in self.iter() {
+            match *src {
+                Source::Url(ref url) => {
                     next!().set_url(&url.url);
                     for hint in url.format_hints.iter() {
                         next!().set_font_format(&hint);
                     }
                 }
-                Source::Local(family) => {
+                Source::Local(ref family) => {
                     next!().set_local_font(&family.name);
                 }
             }
         }
         debug_assert!(target_srcs.next().is_none(), "Should have filled all slots");
     }
 }
 
-impl ToNsCssValue for Vec<UnicodeRange> {
+impl<'a> ToNsCssValue for &'a Vec<UnicodeRange> {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let target_ranges = nscssvalue
             .set_array((self.len() * 2) as i32)
             .as_mut_slice().chunks_mut(2);
         for (range, target) in self.iter().zip(target_ranges) {
             target[0].set_integer(range.start as i32);
             target[1].set_integer(range.end as i32);
         }
     }
 }
 
-impl ToNsCssValue for FontDisplay {
+impl<'a> ToNsCssValue for &'a FontDisplay {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        nscssvalue.set_enum(match self {
+        nscssvalue.set_enum(match *self {
             FontDisplay::Auto => structs::NS_FONT_DISPLAY_AUTO,
             FontDisplay::Block => structs::NS_FONT_DISPLAY_BLOCK,
             FontDisplay::Swap => structs::NS_FONT_DISPLAY_SWAP,
             FontDisplay::Fallback => structs::NS_FONT_DISPLAY_FALLBACK,
             FontDisplay::Optional => structs::NS_FONT_DISPLAY_OPTIONAL,
         } as i32)
     }
 }
 
-impl FontFaceRule {
-    /// Ask Gecko to deep clone the nsCSSFontFaceRule, and then construct
-    /// a FontFaceRule object from it.
-    pub fn deep_clone_from_gecko(&self) -> FontFaceRule {
-        let result = unsafe {
-            UniqueRefPtr::from_addrefed(
-                bindings::Gecko_CSSFontFaceRule_Clone(self.get()))
-        };
-        result.get()
-    }
-}
-
-impl From<FontFaceRuleData> for FontFaceRule {
-    fn from(data: FontFaceRuleData) -> FontFaceRule {
-        let mut result = unsafe {
-            UniqueRefPtr::from_addrefed(bindings::Gecko_CSSFontFaceRule_Create(
-                data.source_location.line as u32, data.source_location.column as u32
-            ))
-        };
-        data.set_descriptors(&mut result.mDecl.mDescriptors);
-        result.get()
-    }
-}
-
-impl ToCssWithGuard for FontFaceRule {
-    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
-        let mut css_text = nsString::new();
-        unsafe {
-            bindings::Gecko_CSSFontFaceRule_GetCssText(self.get(), &mut *css_text);
-        }
-        write!(dest, "{}", css_text)
-    }
-}
-
 /// A @counter-style rule
 pub type CounterStyleRule = RefPtr<nsCSSCounterStyleRule>;
 
 impl CounterStyleRule {
     /// Ask Gecko to deep clone the nsCSSCounterStyleRule, and then construct
     /// a CounterStyleRule object from it.
     pub fn deep_clone_from_gecko(&self) -> CounterStyleRule {
         let result = unsafe {
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -255,18 +255,16 @@ macro_rules! impl_refcount {
             }
             unsafe fn release(&self) {
                 ::gecko_bindings::bindings::$release(self as *const _ as *mut _)
             }
         }
     );
 }
 
-impl_refcount!(::gecko_bindings::structs::nsCSSFontFaceRule,
-               Gecko_CSSFontFaceRule_AddRef, Gecko_CSSFontFaceRule_Release);
 impl_refcount!(::gecko_bindings::structs::nsCSSCounterStyleRule,
                Gecko_CSSCounterStyleRule_AddRef, Gecko_CSSCounterStyleRule_Release);
 
 // Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
 //
 // Gets you a free RefCounted impl implemented via FFI.
 macro_rules! impl_threadsafe_refcount {
     ($t:ty, $addref:ident, $release:ident) => (
--- a/servo/components/style/stylesheets/font_face_rule.rs
+++ b/servo/components/style/stylesheets/font_face_rule.rs
@@ -1,24 +1,7 @@
 /* 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/. */
 
-// TODO(emilio): unify this, components/style/font_face.rs, and
-// components/style/gecko/rules.rs
 #![allow(missing_docs)]
 
-#[cfg(feature = "servo")]
 pub use font_face::FontFaceRuleData as FontFaceRule;
-#[cfg(feature = "gecko")]
-pub use gecko::rules::FontFaceRule;
-
-impl FontFaceRule {
-    #[cfg(feature = "servo")]
-    pub fn clone_conditionally_gecko_or_servo(&self) -> FontFaceRule {
-        self.clone()
-    }
-
-    #[cfg(feature = "gecko")]
-    pub fn clone_conditionally_gecko_or_servo(&self) -> FontFaceRule {
-        self.deep_clone_from_gecko()
-    }
-}
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -301,18 +301,17 @@ impl DeepCloneWithLock for CssRule {
             },
             CssRule::Media(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::Media(Arc::new(
                     lock.wrap(rule.deep_clone_with_lock(lock, guard, params))))
             },
             CssRule::FontFace(ref arc) => {
                 let rule = arc.read_with(guard);
-                CssRule::FontFace(Arc::new(lock.wrap(
-                    rule.clone_conditionally_gecko_or_servo())))
+                CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::FontFeatureValues(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::FontFeatureValues(Arc::new(lock.wrap(rule.clone())))
             },
             CssRule::CounterStyle(ref arc) => {
                 let rule = arc.read_with(guard);
                 CssRule::CounterStyle(Arc::new(lock.wrap(
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -1607,17 +1607,17 @@ pub struct ExtraStyleData {
     pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
 
     /// A map of effective page rules.
     #[cfg(feature = "gecko")]
     pub pages: Vec<Arc<Locked<PageRule>>>,
 }
 
 // FIXME(emilio): This is kind of a lie, and relies on us not cloning
-// nsCSSFontFaceRules or nsCSSCounterStyleRules OMT (which we don't).
+// nsCSSCounterStyleRules OMT (which we don't).
 #[cfg(feature = "gecko")]
 unsafe impl Sync for ExtraStyleData {}
 #[cfg(feature = "gecko")]
 unsafe impl Send for ExtraStyleData {}
 
 #[cfg(feature = "gecko")]
 impl ExtraStyleData {
     /// Add the given @font-face rule.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1,13 +1,13 @@
 /* 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/. */
 
-use cssparser::{ParseErrorKind, Parser, ParserInput};
+use cssparser::{ParseErrorKind, Parser, ParserInput, SourceLocation};
 use cssparser::ToCss as ParserToCss;
 use env_logger::Builder;
 use malloc_size_of::MallocSizeOfOps;
 use selectors::NthIndexCache;
 use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
 use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
 use smallvec::SmallVec;
 use std::cell::RefCell;
@@ -36,16 +36,17 @@ use style::gecko::traversal::RecalcStyle
 use style::gecko::wrapper::{GeckoElement, GeckoNode};
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::{RawGeckoElementBorrowed, RawGeckoElementBorrowedOrNull, RawGeckoNodeBorrowed};
 use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut};
 use style::gecko_bindings::bindings::{RawServoAuthorStyles, RawServoAuthorStylesBorrowed};
 use style::gecko_bindings::bindings::{RawServoAuthorStylesBorrowedMut, RawServoAuthorStylesOwned};
 use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong};
 use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed};
+use style::gecko_bindings::bindings::{RawServoFontFaceRuleBorrowed, RawServoFontFaceRuleStrong};
 use style::gecko_bindings::bindings::{RawServoFontFeatureValuesRule, RawServoFontFeatureValuesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoImportRule, RawServoImportRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframeBorrowed, RawServoKeyframeStrong};
 use style::gecko_bindings::bindings::{RawServoKeyframesRule, RawServoKeyframesRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoMediaListBorrowed, RawServoMediaListStrong};
 use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed};
 use style::gecko_bindings::bindings::{RawServoPageRule, RawServoPageRuleBorrowed};
@@ -86,27 +87,28 @@ use style::gecko_bindings::bindings::nsC
 use style::gecko_bindings::bindings::nsTArrayBorrowed_uintptr_t;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowed;
 use style::gecko_bindings::bindings::nsTimingFunctionBorrowedMut;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{CallerType, CSSPseudoElementType, CompositeOperation};
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{RawServoStyleRule, ComputedStyleStrong, RustString};
 use style::gecko_bindings::structs::{ServoStyleSheet, SheetLoadData, SheetParsingMode, nsAtom, nsCSSPropertyID};
-use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSFontFaceRule, nsCSSCounterStyleRule};
+use style::gecko_bindings::structs::{nsCSSFontDesc, nsCSSCounterStyleRule};
 use style::gecko_bindings::structs::{nsRestyleHint, nsChangeHint, PropertyValuePair};
 use style::gecko_bindings::structs::AtomArray;
 use style::gecko_bindings::structs::IterationCompositeOperation;
 use style::gecko_bindings::structs::MallocSizeOf as GeckoMallocSizeOf;
 use style::gecko_bindings::structs::OriginFlags;
 use style::gecko_bindings::structs::OriginFlags_Author;
 use style::gecko_bindings::structs::OriginFlags_User;
 use style::gecko_bindings::structs::OriginFlags_UserAgent;
 use style::gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 use style::gecko_bindings::structs::RawGeckoPresContextOwned;
+use style::gecko_bindings::structs::RawServoFontFaceRule;
 use style::gecko_bindings::structs::RawServoSelectorList;
 use style::gecko_bindings::structs::RawServoSourceSizeList;
 use style::gecko_bindings::structs::SeenPtrs;
 use style::gecko_bindings::structs::ServoElementSnapshotTable;
 use style::gecko_bindings::structs::ServoStyleSetSizes;
 use style::gecko_bindings::structs::ServoTraversalFlags;
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::URLExtraData;
@@ -135,18 +137,18 @@ use style::properties::animated_properti
 use style::properties::animated_properties::compare_property_priority;
 use style::rule_cache::RuleCacheConditions;
 use style::rule_tree::{CascadeLevel, StrongRuleNode, StyleSource};
 use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::{Atom, WeakAtom};
 use style::style_adjuster::StyleAdjuster;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
-use style::stylesheets::{FontFeatureValuesRule, ImportRule, KeyframesRule, MediaRule};
-use style::stylesheets::{NamespaceRule, Origin, OriginSet, PageRule, StyleRule};
+use style::stylesheets::{FontFaceRule, FontFeatureValuesRule, ImportRule, KeyframesRule};
+use style::stylesheets::{MediaRule, NamespaceRule, Origin, OriginSet, PageRule, StyleRule};
 use style::stylesheets::{StylesheetContents, SupportsRule};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 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;
@@ -1741,16 +1743,22 @@ impl_group_rule_funcs! { (Document, Docu
 }
 
 impl_basic_rule_funcs! { (FontFeatureValues, FontFeatureValuesRule, RawServoFontFeatureValuesRule),
     getter: Servo_CssRules_GetFontFeatureValuesRuleAt,
     debug: Servo_FontFeatureValuesRule_Debug,
     to_css: Servo_FontFeatureValuesRule_GetCssText,
 }
 
+impl_basic_rule_funcs! { (FontFace, FontFaceRule, RawServoFontFaceRule),
+    getter: Servo_CssRules_GetFontFaceRuleAt,
+    debug: Servo_FontFaceRule_Debug,
+    to_css: Servo_FontFaceRule_GetCssText,
+}
+
 macro_rules! impl_getter_for_embedded_rule {
     ($getter:ident: $name:ident -> $ty:ty) => {
         #[no_mangle]
         pub extern "C" fn $getter(rules: ServoCssRulesBorrowed, index: u32) -> *mut $ty
         {
             let global_style_data = &*GLOBAL_STYLE_DATA;
             let guard = global_style_data.shared_lock.read();
             let rules = Locked::<CssRules>::as_arc(&rules).read_with(&guard);
@@ -1758,18 +1766,16 @@ macro_rules! impl_getter_for_embedded_ru
                 CssRule::$name(ref rule) => rule.read_with(&guard).get(),
                 _ => unreachable!(concat!(stringify!($getter), " should only be called on a ",
                                           stringify!($name), " rule")),
             }
         }
     }
 }
 
-impl_getter_for_embedded_rule!(Servo_CssRules_GetFontFaceRuleAt:
-                               FontFace -> nsCSSFontFaceRule);
 impl_getter_for_embedded_rule!(Servo_CssRules_GetCounterStyleRuleAt:
                                CounterStyle -> nsCSSCounterStyleRule);
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_GetStyle(rule: RawServoStyleRuleBorrowed) -> RawServoDeclarationBlockStrong {
     read_locked_arc(rule, |rule: &StyleRule| {
         rule.block.clone().into_strong()
     })
@@ -2179,16 +2185,265 @@ pub extern "C" fn Servo_FontFeatureValue
     result: *mut nsAString,
 ) {
     read_locked_arc(rule, |rule: &FontFeatureValuesRule| {
         rule.value_to_css(&mut CssWriter::new(unsafe { result.as_mut().unwrap() })).unwrap();
     })
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_FontFaceRule_CreateEmpty() -> RawServoFontFaceRuleStrong {
+    let global_style_data = &*GLOBAL_STYLE_DATA;
+    // XXX This is not great. We should split FontFace descriptor data
+    // from the rule, so that we don't need to create the rule like this
+    // and the descriptor data itself can be hold in UniquePtr from the
+    // Gecko side. See bug 1450904.
+    Arc::new(global_style_data.shared_lock.wrap(FontFaceRule::empty(SourceLocation {
+        line: 0,
+        column: 0,
+    }))).into_strong()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_Clone(
+    rule: RawServoFontFaceRuleBorrowed,
+) -> RawServoFontFaceRuleStrong {
+    let clone = read_locked_arc(rule, |rule: &FontFaceRule| rule.clone());
+    let global_style_data = &*GLOBAL_STYLE_DATA;
+    Arc::new(global_style_data.shared_lock.wrap(clone)).into_strong()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetSourceLocation(
+    rule: RawServoFontFaceRuleBorrowed,
+    line: *mut u32,
+    column: *mut u32,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let location = rule.source_location;
+        *line.as_mut().unwrap() = location.line as u32;
+        *column.as_mut().unwrap() = location.column as u32;
+    });
+}
+
+macro_rules! apply_font_desc_list {
+    ($apply_macro:ident) => {
+        $apply_macro! {
+            valid: [
+                eCSSFontDesc_Family => family,
+                eCSSFontDesc_Style => style,
+                eCSSFontDesc_Weight => weight,
+                eCSSFontDesc_Stretch => stretch,
+                eCSSFontDesc_Src => sources,
+                eCSSFontDesc_UnicodeRange => unicode_range,
+                eCSSFontDesc_FontFeatureSettings => feature_settings,
+                eCSSFontDesc_FontVariationSettings => variation_settings,
+                eCSSFontDesc_FontLanguageOverride => language_override,
+                eCSSFontDesc_Display => display,
+            ]
+            invalid: [
+                eCSSFontDesc_UNKNOWN,
+                eCSSFontDesc_COUNT,
+            ]
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_Length(
+    rule: RawServoFontFaceRuleBorrowed,
+) -> u32 {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let mut result = 0;
+        macro_rules! count_values {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                $(if rule.$field.is_some() {
+                    result += 1;
+                })*
+            }
+        }
+        apply_font_desc_list!(count_values);
+        result
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_IndexGetter(
+    rule: RawServoFontFaceRuleBorrowed,
+    index: u32,
+) -> nsCSSFontDesc {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let mut count = 0;
+        macro_rules! lookup_index {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                $(if rule.$field.is_some() {
+                    count += 1;
+                    if count - 1 == index {
+                        return nsCSSFontDesc::$v_enum_name;
+                    }
+                })*
+            }
+        }
+        apply_font_desc_list!(lookup_index);
+        return nsCSSFontDesc::eCSSFontDesc_UNKNOWN;
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDeclCssText(
+    rule: RawServoFontFaceRuleBorrowed,
+    result: *mut nsAString,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        rule.decl_to_css(result.as_mut().unwrap()).unwrap();
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDescriptor(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+    result: nsCSSValueBorrowedMut,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        macro_rules! to_css_value {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(
+                        nsCSSFontDesc::$v_enum_name => {
+                            if let Some(ref value) = rule.$field {
+                                result.set_from(value);
+                            }
+                        }
+                    )*
+                    $(
+                        nsCSSFontDesc::$i_enum_name => {
+                            debug_assert!(false, "not a valid font descriptor");
+                        }
+                    )*
+                }
+            }
+        }
+        apply_font_desc_list!(to_css_value)
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_GetDescriptorCssText(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+    result: *mut nsAString,
+) {
+    read_locked_arc(rule, |rule: &FontFaceRule| {
+        let mut writer = CssWriter::new(result.as_mut().unwrap());
+        macro_rules! to_css_text {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(
+                        nsCSSFontDesc::$v_enum_name => {
+                            if let Some(ref value) = rule.$field {
+                                value.to_css(&mut writer).unwrap();
+                            }
+                        }
+                    )*
+                    $(
+                        nsCSSFontDesc::$i_enum_name => {
+                            debug_assert!(false, "not a valid font descriptor");
+                        }
+                    )*
+                }
+            }
+        }
+        apply_font_desc_list!(to_css_text)
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_SetDescriptor(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+    value: *const nsACString,
+    data: *mut URLExtraData,
+) -> bool {
+    let value = value.as_ref().unwrap().as_str_unchecked();
+    let mut input = ParserInput::new(&value);
+    let mut parser = Parser::new(&mut input);
+    let url_data = RefPtr::from_ptr_ref(&data);
+    let context = ParserContext::new(
+        Origin::Author,
+        url_data,
+        Some(CssRuleType::FontFace),
+        ParsingMode::DEFAULT,
+        QuirksMode::NoQuirks,
+    );
+
+    write_locked_arc(rule, |rule: &mut FontFaceRule| {
+        macro_rules! to_css_text {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(
+                        nsCSSFontDesc::$v_enum_name => {
+                            if let Ok(value) = parser.parse_entirely(|i| Parse::parse(&context, i)) {
+                                rule.$field = Some(value);
+                                true
+                            } else {
+                                false
+                            }
+                        }
+                    )*
+                    $(
+                        nsCSSFontDesc::$i_enum_name => {
+                            debug_assert!(false, "not a valid font descriptor");
+                            false
+                        }
+                    )*
+                }
+            }
+        }
+        apply_font_desc_list!(to_css_text)
+    })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_FontFaceRule_ResetDescriptor(
+    rule: RawServoFontFaceRuleBorrowed,
+    desc: nsCSSFontDesc,
+) {
+    write_locked_arc(rule, |rule: &mut FontFaceRule| {
+        macro_rules! reset_desc {
+            (
+                valid: [$($v_enum_name:ident => $field:ident,)*]
+                invalid: [$($i_enum_name:ident,)*]
+            ) => {
+                match desc {
+                    $(nsCSSFontDesc::$v_enum_name => rule.$field = None,)*
+                    $(nsCSSFontDesc::$i_enum_name => debug_assert!(false, "not a valid font descriptor"),)*
+                }
+            }
+        }
+        apply_font_desc_list!(reset_desc)
+    })
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_ComputedValues_GetForAnonymousBox(
     parent_style_or_null: ComputedStyleBorrowedOrNull,
     pseudo_tag: *mut nsAtom,
     raw_data: RawServoStyleSetBorrowed,
 ) -> ComputedStyleStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     let guards = StylesheetGuards::same(&guard);
@@ -4325,35 +4580,32 @@ pub unsafe extern "C" fn Servo_StyleSet_
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_GetFontFaceRules(
     raw_data: RawServoStyleSetBorrowed,
     rules: RawGeckoFontFaceRuleListBorrowedMut,
 ) {
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
     debug_assert_eq!(rules.len(), 0);
 
-    let global_style_data = &*GLOBAL_STYLE_DATA;
-    let guard = global_style_data.shared_lock.read();
-
     let len: u32 = data
         .stylist
         .iter_extra_data_origins()
         .map(|(d, _)| d.font_faces.len() as u32)
         .sum();
 
     // Reversed iterator because Gecko expects rules to appear sorted
     // UserAgent first, Author last.
     let font_face_iter = data
         .stylist
         .iter_extra_data_origins_rev()
         .flat_map(|(d, o)| d.font_faces.iter().zip(iter::repeat(o)));
 
     unsafe { rules.set_len(len) };
     for (src, dest) in font_face_iter.zip(rules.iter_mut()) {
-        dest.mRule = src.0.read_with(&guard).clone().forget();
+        dest.mRule.set_arc_leaky(src.0.clone());
         dest.mSheetType = src.1.into();
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_GetCounterStyleRule(
     raw_data: RawServoStyleSetBorrowed,
     name: *mut nsAtom,
@@ -4843,93 +5095,16 @@ pub extern "C" fn Servo_ParseTransformIn
 
     let result = unsafe { result.as_mut() }.expect("not a valid matrix");
     let contain_3d = unsafe { contain_3d.as_mut() }.expect("not a valid bool");
     *result = m.to_row_major_array();
     *contain_3d = is_3d;
     true
 }
 
-// https://drafts.csswg.org/css-font-loading/#dom-fontface-fontface
-#[no_mangle]
-pub extern "C" fn Servo_ParseFontDescriptor(
-    desc_id: nsCSSFontDesc,
-    value: *const nsAString,
-    data: *mut URLExtraData,
-    result: nsCSSValueBorrowedMut,
-) -> bool {
-    use cssparser::UnicodeRange;
-    use self::nsCSSFontDesc::*;
-    use style::computed_values::{font_stretch, font_style};
-    use style::font_face::{FontDisplay, FontWeight, Source};
-    use style::properties::longhands::font_language_override;
-    use style::values::computed::font::FamilyName;
-    use style::values::specified::font::{SpecifiedFontFeatureSettings, SpecifiedFontVariationSettings};
-
-    let string = unsafe { (*value).to_string() };
-    let mut input = ParserInput::new(&string);
-    let mut parser = Parser::new(&mut input);
-    let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
-    let context = ParserContext::new(
-        Origin::Author,
-        url_data,
-        Some(CssRuleType::FontFace),
-        ParsingMode::DEFAULT,
-        QuirksMode::NoQuirks,
-    );
-
-    macro_rules! parse_font_desc {
-        (
-            valid = [ $( $v_enum_name: ident / $t: ty, )* ]
-            invalid = [ $( $i_enum_name: ident, )* ]
-        ) => {
-            match desc_id {
-                $(
-                    $v_enum_name => {
-                        let f = match parser.parse_entirely(|i| <$t as Parse>::parse(&context, i)) {
-                            Ok(f) => f,
-                            Err(..) => return false,
-                        };
-                        result.set_from(f);
-                    },
-                )*
-                $(
-                    $i_enum_name => {
-                        debug_assert!(false, "$i_enum_name is not a valid font descriptor");
-                        return false;
-                    },
-                )*
-            }
-        }
-    }
-
-    // We implement the parser of each arm according to the implementation of @font-face rule.
-    // see component/style/font_face.rs for more detail.
-    parse_font_desc!(
-        valid = [
-            eCSSFontDesc_Family / FamilyName,
-            eCSSFontDesc_Style / font_style::T,
-            eCSSFontDesc_Weight / FontWeight,
-            eCSSFontDesc_Stretch / font_stretch::T,
-            eCSSFontDesc_Src / Vec<Source>,
-            eCSSFontDesc_UnicodeRange / Vec<UnicodeRange>,
-            eCSSFontDesc_FontFeatureSettings / SpecifiedFontFeatureSettings,
-            eCSSFontDesc_FontVariationSettings / SpecifiedFontVariationSettings,
-            eCSSFontDesc_FontLanguageOverride / font_language_override::SpecifiedValue,
-            eCSSFontDesc_Display / FontDisplay,
-        ]
-        invalid = [
-            eCSSFontDesc_UNKNOWN,
-            eCSSFontDesc_COUNT,
-        ]
-    );
-
-    true
-}
-
 #[no_mangle]
 pub extern "C" fn Servo_ParseFontShorthandForMatching(
     value: *const nsAString,
     data: *mut URLExtraData,
     family: *mut structs::RefPtr<structs::SharedFontList>,
     style: nsCSSValueBorrowedMut,
     stretch: nsCSSValueBorrowedMut,
     weight: nsCSSValueBorrowedMut
@@ -4957,21 +5132,21 @@ pub extern "C" fn Servo_ParseFontShortha
 
     // The system font is not acceptable, so we return false.
     let family = unsafe { &mut *family };
     match font.font_family {
         FontFamily::Values(list) => family.set_move(list.0),
         FontFamily::System(_) => return false,
     }
     style.set_from(match font.font_style {
-        font_style::SpecifiedValue::Keyword(kw) => kw,
+        font_style::SpecifiedValue::Keyword(ref kw) => kw,
         font_style::SpecifiedValue::System(_) => return false,
     });
     stretch.set_from(match font.font_stretch {
-        font_stretch::SpecifiedValue::Keyword(kw) => kw,
+        font_stretch::SpecifiedValue::Keyword(ref kw) => kw,
         font_stretch::SpecifiedValue::System(_) => return false,
     });
     match font.font_weight {
         FontWeight::Weight(w) => weight.set_from(w),
         FontWeight::Normal => weight.set_enum(structs::NS_STYLE_FONT_WEIGHT_NORMAL as i32),
         FontWeight::Bold => weight.set_enum(structs::NS_STYLE_FONT_WEIGHT_BOLD as i32),
         // Resolve relative font weights against the initial of font-weight
         // (normal, which is equivalent to 400).