Bug 1449087 part 2 - Use Servo data to back @font-face rule. r=emilio
authorXidorn Quan <me@upsuper.org>
Wed, 04 Apr 2018 08:42:10 +1000
changeset 411643 8d23659e5494408256229f2ef9ddc65194a95489
parent 411642 233075da24e319daf7fdfa568812fed60c091539
child 411644 d00d3cc678fdeab9b94b69aa759959a8dd45e35f
push id101712
push userebalazs@mozilla.com
push dateWed, 04 Apr 2018 09:52:15 +0000
treeherdermozilla-inbound@dd31fb345c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1449087
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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
--- 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,21 +8,22 @@
 
 #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 "mozilla/StaticPrefs.h"
-#include "nsCSSFontFaceRule.h"
 #include "nsCSSParser.h"
 #include "nsIDocument.h"
 #include "nsStyleUtil.h"
 
 namespace mozilla {
 namespace dom {
 
 // -- FontFaceBufferSource ---------------------------------------------------
@@ -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,121 @@ 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) ||
       (StaticPrefs::layout_css_font_variations_enabled() &&
-       !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 +679,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/StaticPrefs.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/LoadInfo.h"
 #include "nsAutoPtr.h"
 #include "nsContentPolicyUtils.h"
@@ -676,17 +677,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);
   }
 
@@ -707,24 +708,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);
     }
@@ -1079,17 +1080,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;
@@ -1193,31 +1196,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();
     }
   }
@@ -1281,34 +1284,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
@@ -124,17 +124,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>
@@ -266,17 +266,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(const gfxFontFaceSrc& aSrc);
--- 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, nsCSSValueBorrowedMut 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/StaticPrefs.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,248 @@
+/* -*- 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
+{
+  MOZ_ASSERT(aResult.IsEmpty());
+  Servo_FontFaceRule_GetDescriptorCssText(mRawRule, aFontDescID, &aResult);
+}
+
+void
+ServoFontFaceRuleDecl::GetCssText(nsAString& aCssText)
+{
+  aCssText.Truncate();
+  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)
+{
+  aResult.Truncate();
+  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");
+
+  aResult.Truncate();
+  if (descID != eCSSFontDesc_UNKNOWN) {
+    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
+{
+  aCssText.Truncate();
+  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',
@@ -176,17 +176,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',
@@ -207,16 +206,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;