Bug 1480615 - reuse scaled fonts across blob image recordings. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Wed, 05 Sep 2018 21:55:53 -0400
changeset 477416 ada6c9fc290cbf335597c6f435eb9e961bda603c
parent 477415 5d90cbaeceb8b76ff4e2a6707aef34e24cdb8361
child 477417 5f5d7a3ce3326619529322a3d8c3f7fffeb06f5b
push idunknown
push userunknown
push dateunknown
reviewersjrmuizel
bugs1480615
milestone64.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 1480615 - reuse scaled fonts across blob image recordings. r=jrmuizel
gfx/2d/2D.h
gfx/2d/DrawEventRecorder.cpp
gfx/2d/DrawEventRecorder.h
gfx/2d/DrawTargetRecording.cpp
gfx/2d/InlineTranslator.h
gfx/2d/Logging.h
gfx/2d/RecordedEvent.cpp
gfx/2d/RecordedEvent.h
gfx/2d/RecordedEventImpl.h
gfx/2d/ScaledFontDWrite.cpp
gfx/2d/ScaledFontDWrite.h
gfx/2d/ScaledFontFontconfig.cpp
gfx/2d/ScaledFontFontconfig.h
gfx/2d/UnscaledFontDWrite.h
gfx/2d/UnscaledFontFreeType.h
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderDrawEventRecorder.h
gfx/layers/wr/WebRenderUserData.h
gfx/webrender_bindings/Moz2DImageRenderer.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/moz2d_renderer.rs
gfx/webrender_bindings/webrender_ffi.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -819,16 +819,26 @@ public:
                      const uint8_t* aInstanceData,
                      uint32_t aInstanceDataLength,
                      const FontVariation* aVariations,
                      uint32_t aNumVariations)
   {
     return nullptr;
   }
 
+  virtual already_AddRefed<ScaledFont>
+    CreateScaledFontFromWRFont(Float aGlyphSize,
+                               const wr::FontInstanceOptions* aOptions,
+                               const wr::FontInstancePlatformOptions* aPlatformOptions,
+                               const FontVariation* aVariations,
+                               uint32_t aNumVariations)
+  {
+    return CreateScaledFont(aGlyphSize, nullptr, 0, aVariations, aNumVariations);
+  }
+
 protected:
   UnscaledFont() {}
 
 private:
   static Atomic<uint32_t> sDeletionCounter;
 };
 
 /** This class is an abstraction of a backend/platform specific font object
--- a/gfx/2d/DrawEventRecorder.cpp
+++ b/gfx/2d/DrawEventRecorder.cpp
@@ -132,17 +132,17 @@ DrawEventRecorderMemory::FlushItem(IntRe
   // destruction events to our stream so we need to do that
   // first.
   DetachResources();
 
   // See moz2d_renderer.rs for a description of the stream format
   WriteElement(mIndex, mOutputStream.mLength);
 
   // write out the fonts into the extra data section
-  mSerializeCallback(mOutputStream, mUnscaledFonts);
+  mSerializeCallback(mOutputStream, mScaledFonts);
   WriteElement(mIndex, mOutputStream.mLength);
 
   WriteElement(mIndex, aRect.x);
   WriteElement(mIndex, aRect.y);
   WriteElement(mIndex, aRect.XMost());
   WriteElement(mIndex, aRect.YMost());
   ClearResources();
 
--- a/gfx/2d/DrawEventRecorder.h
+++ b/gfx/2d/DrawEventRecorder.h
@@ -41,20 +41,19 @@ public:
       auto oldSurface = surface++;
       (*oldSurface)->RemoveUserData(reinterpret_cast<UserDataKey*>(this));
     }
     mStoredFonts.clear();
     mStoredSurfaces.clear();
   }
 
   void ClearResources() {
-    mUnscaledFonts.clear();
     mStoredObjects.clear();
     mStoredFontData.clear();
-    mUnscaledFontMap.clear();
+    mScaledFonts.clear();
   }
 
   template<class S>
   void WriteHeader(S& aStream) {
     WriteElement(aStream, kMagicInt);
     WriteElement(aStream, kMajorRevision);
     WriteElement(aStream, kMinorRevision);
   }
@@ -66,17 +65,20 @@ public:
     mStoredObjects.insert(aObject);
   }
 
   void RemoveStoredObject(const ReferencePtr aObject) {
     mStoredObjects.erase(aObject);
   }
 
   void AddScaledFont(ScaledFont* aFont) {
-    mStoredFonts.insert(aFont);
+    if (mStoredFonts.insert(aFont).second &&
+        WantsExternalFonts()) {
+      mScaledFonts.push_back(aFont);
+    }
   }
 
   void RemoveScaledFont(ScaledFont* aFont) {
     mStoredFonts.erase(aFont);
   }
 
   void AddSourceSurface(SourceSurface* aSurface) {
     mStoredSurfaces.insert(aSurface);
@@ -93,30 +95,16 @@ public:
   void AddStoredFontData(const uint64_t aFontDataKey) {
     mStoredFontData.insert(aFontDataKey);
   }
 
   bool HasStoredFontData(const uint64_t aFontDataKey) {
     return mStoredFontData.find(aFontDataKey) != mStoredFontData.end();
   }
 
-  // Returns the index of the UnscaledFont
-  size_t GetUnscaledFontIndex(UnscaledFont *aFont) {
-    auto i = mUnscaledFontMap.find(aFont);
-    size_t index;
-    if (i == mUnscaledFontMap.end()) {
-      mUnscaledFonts.push_back(aFont);
-      index = mUnscaledFonts.size() - 1;
-      mUnscaledFontMap.insert({{aFont, index}});
-    } else {
-      index = i->second;
-    }
-    return index;
-  }
-
   bool WantsExternalFonts() const { return mExternalFonts; }
 
   void TakeExternalSurfaces(std::vector<RefPtr<SourceSurface>>& aSurfaces)
   {
     aSurfaces = std::move(mExternalSurfaces);
   }
 
   virtual void StoreSourceSurfaceRecording(SourceSurface *aSurface,
@@ -126,19 +114,18 @@ protected:
   void StoreExternalSurfaceRecording(SourceSurface* aSurface,
                                      uint64_t aKey);
 
   virtual void Flush() = 0;
 
   std::unordered_set<const void*> mStoredObjects;
   std::unordered_set<uint64_t> mStoredFontData;
   std::unordered_set<ScaledFont*> mStoredFonts;
+  std::vector<RefPtr<ScaledFont>> mScaledFonts;
   std::unordered_set<SourceSurface*> mStoredSurfaces;
-  std::vector<RefPtr<UnscaledFont>> mUnscaledFonts;
-  std::unordered_map<UnscaledFont*, size_t> mUnscaledFontMap;
   std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
   bool mExternalFonts;
 };
 
 class DrawEventRecorderFile : public DrawEventRecorderPrivate
 {
   using char_type = filesystem::Path::value_type;
 public:
@@ -168,17 +155,17 @@ public:
   void Close();
 
 private:
   void Flush() override;
 
   mozilla::OFStream mOutputStream;
 };
 
-typedef std::function<void(MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts)> SerializeResourcesFn;
+typedef std::function<void(MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts)> SerializeResourcesFn;
 
 // WARNING: This should not be used in its existing state because
 // it is likely to OOM because of large continguous allocations.
 class DrawEventRecorderMemory : public DrawEventRecorderPrivate
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory, override)
 
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -302,47 +302,44 @@ void
 DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
                                 const GlyphBuffer &aBuffer,
                                 const Pattern &aPattern,
                                 const DrawOptions &aOptions)
 {
   EnsurePatternDependenciesStored(aPattern);
 
   UserDataKey* userDataKey = reinterpret_cast<UserDataKey*>(mRecorder.get());
-  if (!aFont->GetUserData(userDataKey)) {
+  if (mRecorder->WantsExternalFonts()) {
+    mRecorder->AddScaledFont(aFont);
+  } else if (!aFont->GetUserData(userDataKey)) {
     UnscaledFont* unscaledFont = aFont->GetUnscaledFont();
-    if (mRecorder->WantsExternalFonts()) {
-      size_t index = mRecorder->GetUnscaledFontIndex(unscaledFont);
-      mRecorder->RecordEvent(RecordedScaledFontCreationByIndex(aFont, index));
-    } else {
-      if (!mRecorder->HasStoredObject(unscaledFont)) {
-	RecordedFontData fontData(unscaledFont);
-	RecordedFontDetails fontDetails;
-	if (fontData.GetFontDetails(fontDetails)) {
-	  // Try to serialise the whole font, just in case this is a web font that
-	  // is not present on the system.
-	  if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) {
-	    mRecorder->RecordEvent(fontData);
-	    mRecorder->AddStoredFontData(fontDetails.fontDataKey);
-	  }
-	  mRecorder->RecordEvent(RecordedUnscaledFontCreation(unscaledFont, fontDetails));
-	} else {
-	  // If that fails, record just the font description and try to load it from
-	  // the system on the other side.
-	  RecordedFontDescriptor fontDesc(unscaledFont);
-	  if (fontDesc.IsValid()) {
-	    mRecorder->RecordEvent(fontDesc);
-	  } else {
-	    gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise UnscaledFont";
-	  }
-	}
-	mRecorder->AddStoredObject(unscaledFont);
+    if (!mRecorder->HasStoredObject(unscaledFont)) {
+      RecordedFontData fontData(unscaledFont);
+      RecordedFontDetails fontDetails;
+      if (fontData.GetFontDetails(fontDetails)) {
+        // Try to serialise the whole font, just in case this is a web font that
+        // is not present on the system.
+        if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) {
+          mRecorder->RecordEvent(fontData);
+          mRecorder->AddStoredFontData(fontDetails.fontDataKey);
+        }
+        mRecorder->RecordEvent(RecordedUnscaledFontCreation(unscaledFont, fontDetails));
+      } else {
+        // If that fails, record just the font description and try to load it from
+        // the system on the other side.
+        RecordedFontDescriptor fontDesc(unscaledFont);
+        if (fontDesc.IsValid()) {
+          mRecorder->RecordEvent(fontDesc);
+        } else {
+          gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise UnscaledFont";
+        }
       }
-      mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, unscaledFont));
+      mRecorder->AddStoredObject(unscaledFont);
     }
+    mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, unscaledFont));
     RecordingFontUserData *userData = new RecordingFontUserData;
     userData->refPtr = aFont;
     userData->recorder = mRecorder;
     aFont->AddUserData(userDataKey, userData, &RecordingFontUserDataDestroyFunc);
     userData->recorder->AddScaledFont(aFont);
   }
 
   mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
--- a/gfx/2d/InlineTranslator.h
+++ b/gfx/2d/InlineTranslator.h
@@ -78,22 +78,16 @@ public:
 
   UnscaledFont* LookupUnscaledFont(ReferencePtr aRefPtr) final
   {
     UnscaledFont* result = mUnscaledFonts.GetWeak(aRefPtr);
     MOZ_ASSERT(result);
     return result;
   }
 
-  UnscaledFont* LookupUnscaledFontByIndex(size_t index) final
-  {
-    UnscaledFont* result = mUnscaledFontTable[index];
-    return result;
-  }
-
   NativeFontResource* LookupNativeFontResource(uint64_t aKey) final
   {
     NativeFontResource* result = mNativeFontResources.GetWeak(aKey);
     MOZ_ASSERT(result);
     return result;
   }
 
   void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) final
@@ -123,17 +117,16 @@ public:
 
   void AddScaledFont(ReferencePtr aRefPtr, ScaledFont *aScaledFont) final
   {
     mScaledFonts.Put(aRefPtr, aScaledFont);
   }
 
   void AddUnscaledFont(ReferencePtr aRefPtr, UnscaledFont *aUnscaledFont) final
   {
-    mUnscaledFontTable.push_back(aUnscaledFont);
     mUnscaledFonts.Put(aRefPtr, aUnscaledFont);
   }
 
   void AddNativeFontResource(uint64_t aKey,
                              NativeFontResource *aScaledFontResouce) final
   {
     mNativeFontResources.Put(aKey, aScaledFontResouce);
   }
@@ -181,17 +174,16 @@ public:
   mozilla::gfx::DrawTarget* GetReferenceDrawTarget() final { return mBaseDT; }
 
   void* GetFontContext() final { return mFontContext; }
 
 private:
   RefPtr<DrawTarget> mBaseDT;
   void*              mFontContext;
 
-  std::vector<RefPtr<UnscaledFont>> mUnscaledFontTable;
   nsRefPtrHashtable<nsPtrHashKey<void>, DrawTarget> mDrawTargets;
   nsRefPtrHashtable<nsPtrHashKey<void>, Path> mPaths;
   nsRefPtrHashtable<nsPtrHashKey<void>, SourceSurface> mSourceSurfaces;
   nsRefPtrHashtable<nsPtrHashKey<void>, FilterNode> mFilterNodes;
   nsRefPtrHashtable<nsPtrHashKey<void>, GradientStops> mGradientStops;
   nsRefPtrHashtable<nsPtrHashKey<void>, ScaledFont> mScaledFonts;
   nsRefPtrHashtable<nsPtrHashKey<void>, UnscaledFont> mUnscaledFonts;
   nsRefPtrHashtable<nsUint64HashKey, NativeFontResource> mNativeFontResources;
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -129,16 +129,17 @@ enum class LogReason : int {
   TextureCreation,
   InvalidCacheSurface,
   AlphaWithBasicClient,
   UnbalancedClipStack,
   ProcessingError,
   InvalidDrawTarget,
   NativeFontResourceNotFound,
   UnscaledFontNotFound,
+  ScaledFontNotFound,
   InvalidLayerType,
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger
 {
   // For efficiency, this method exists and copies the logic of the
--- a/gfx/2d/RecordedEvent.cpp
+++ b/gfx/2d/RecordedEvent.cpp
@@ -84,18 +84,16 @@ RecordedEvent::GetEventName(EventType aT
   case GRADIENTSTOPSCREATION:
     return "GradientStopsCreation";
   case GRADIENTSTOPSDESTRUCTION:
     return "GradientStopsDestruction";
   case SNAPSHOT:
     return "Snapshot";
   case SCALEDFONTCREATION:
     return "ScaledFontCreation";
-  case SCALEDFONTCREATIONBYINDEX:
-    return "ScaledFontCreationByIndex";
   case SCALEDFONTDESTRUCTION:
     return "ScaledFontDestruction";
   case MASKSURFACE:
     return "MaskSurface";
   case FILTERNODESETATTRIBUTE:
     return "SetAttribute";
   case FILTERNODESETINPUT:
     return "SetInput";
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -85,17 +85,16 @@ public:
 
   virtual DrawTarget *LookupDrawTarget(ReferencePtr aRefPtr) = 0;
   virtual Path *LookupPath(ReferencePtr aRefPtr) = 0;
   virtual SourceSurface *LookupSourceSurface(ReferencePtr aRefPtr) = 0;
   virtual FilterNode *LookupFilterNode(ReferencePtr aRefPtr) = 0;
   virtual GradientStops *LookupGradientStops(ReferencePtr aRefPtr) = 0;
   virtual ScaledFont *LookupScaledFont(ReferencePtr aRefPtr) = 0;
   virtual UnscaledFont* LookupUnscaledFont(ReferencePtr aRefPtr) = 0;
-  virtual UnscaledFont* LookupUnscaledFontByIndex(size_t aIndex) { return nullptr; }
   virtual NativeFontResource *LookupNativeFontResource(uint64_t aKey) = 0;
   virtual already_AddRefed<SourceSurface> LookupExternalSurface(uint64_t aKey) { return nullptr; }
   virtual void AddDrawTarget(ReferencePtr aRefPtr, DrawTarget *aDT) = 0;
   virtual void RemoveDrawTarget(ReferencePtr aRefPtr) = 0;
   virtual void AddPath(ReferencePtr aRefPtr, Path *aPath) = 0;
   virtual void RemovePath(ReferencePtr aRefPtr) = 0;
   virtual void AddSourceSurface(ReferencePtr aRefPtr, SourceSurface *aPath) = 0;
   virtual void RemoveSourceSurface(ReferencePtr aRefPtr) = 0;
@@ -239,17 +238,16 @@ public:
     PATHCREATION,
     PATHDESTRUCTION,
     SOURCESURFACECREATION,
     SOURCESURFACEDESTRUCTION,
     GRADIENTSTOPSCREATION,
     GRADIENTSTOPSDESTRUCTION,
     SNAPSHOT,
     SCALEDFONTCREATION,
-    SCALEDFONTCREATIONBYINDEX,
     SCALEDFONTDESTRUCTION,
     MASKSURFACE,
     FILTERNODECREATION,
     FILTERNODEDESTRUCTION,
     DRAWFILTER,
     FILTERNODESETATTRIBUTE,
     FILTERNODESETINPUT,
     CREATESIMILARDRAWTARGET,
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -1197,60 +1197,16 @@ private:
   Float mGlyphSize;
   std::vector<uint8_t> mInstanceData;
   std::vector<FontVariation> mVariations;
 
   template<class S>
   MOZ_IMPLICIT RecordedScaledFontCreation(S &aStream);
 };
 
-class RecordedScaledFontCreationByIndex : public RecordedEventDerived<RecordedScaledFontCreationByIndex> {
-public:
-
-  static void FontInstanceDataProc(const uint8_t* aData, uint32_t aSize,
-                                   const FontVariation* aVariations, uint32_t aNumVariations,
-                                   void* aBaton)
-  {
-    auto recordedScaledFontCreation = static_cast<RecordedScaledFontCreationByIndex*>(aBaton);
-    recordedScaledFontCreation->SetFontInstanceData(aData, aSize, aVariations, aNumVariations);
-  }
-
-  RecordedScaledFontCreationByIndex(ScaledFont* aScaledFont, size_t aUnscaledFontIndex)
-    : RecordedEventDerived(SCALEDFONTCREATIONBYINDEX)
-    , mRefPtr(aScaledFont)
-    , mUnscaledFontIndex(aUnscaledFontIndex)
-    , mGlyphSize(aScaledFont->GetSize())
-  {
-    aScaledFont->GetFontInstanceData(FontInstanceDataProc, this);
-  }
-
-  virtual bool PlayEvent(Translator *aTranslator) const override;
-
-  template<class S> void Record(S &aStream) const;
-  virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const override;
-
-  virtual std::string GetName() const override { return "ScaledFont Creation"; }
-  virtual ReferencePtr GetObjectRef() const override { return mRefPtr; }
-
-  void SetFontInstanceData(const uint8_t *aData, uint32_t aSize,
-                           const FontVariation* aVariations, uint32_t aNumVariations);
-
-private:
-  friend class RecordedEvent;
-
-  ReferencePtr mRefPtr;
-  size_t mUnscaledFontIndex;
-  Float mGlyphSize;
-  std::vector<uint8_t> mInstanceData;
-  std::vector<FontVariation> mVariations;
-
-  template<class S>
-  MOZ_IMPLICIT RecordedScaledFontCreationByIndex(S &aStream);
-};
-
 class RecordedScaledFontDestruction : public RecordedEventDerived<RecordedScaledFontDestruction> {
 public:
   MOZ_IMPLICIT RecordedScaledFontDestruction(ReferencePtr aRefPtr)
     : RecordedEventDerived(SCALEDFONTDESTRUCTION), mRefPtr(aRefPtr)
   {
   }
 
   virtual bool PlayEvent(Translator *aTranslator) const override;
@@ -3280,80 +3236,16 @@ RecordedScaledFontCreation::RecordedScal
   aStream.read((char*)mInstanceData.data(), size);
   size_t numVariations;
   ReadElement(aStream, numVariations);
   mVariations.resize(numVariations);
   aStream.read((char*)mVariations.data(), sizeof(FontVariation) * numVariations);
 }
 
 inline bool
-RecordedScaledFontCreationByIndex::PlayEvent(Translator *aTranslator) const
-{
-  UnscaledFont* unscaledFont = aTranslator->LookupUnscaledFontByIndex(mUnscaledFontIndex);
-  if (!unscaledFont) {
-    gfxDevCrash(LogReason::UnscaledFontNotFound) <<
-      "UnscaledFont lookup failed for key |" << hexa(mUnscaledFontIndex) << "|.";
-    return false;
-  }
-
-  RefPtr<ScaledFont> scaledFont =
-    unscaledFont->CreateScaledFont(mGlyphSize,
-                                   mInstanceData.data(), mInstanceData.size(),
-                                   mVariations.data(), mVariations.size());
-
-  aTranslator->AddScaledFont(mRefPtr, scaledFont);
-  return true;
-}
-
-template<class S>
-void
-RecordedScaledFontCreationByIndex::Record(S &aStream) const
-{
-  WriteElement(aStream, mRefPtr);
-  WriteElement(aStream, mUnscaledFontIndex);
-  WriteElement(aStream, mGlyphSize);
-  WriteElement(aStream, (size_t)mInstanceData.size());
-  aStream.write((char*)mInstanceData.data(), mInstanceData.size());
-  WriteElement(aStream, (size_t)mVariations.size());
-  aStream.write((char*)mVariations.data(), sizeof(FontVariation) * mVariations.size());
-}
-
-inline void
-RecordedScaledFontCreationByIndex::OutputSimpleEventInfo(std::stringstream &aStringStream) const
-{
-  aStringStream << "[" << mRefPtr << "] ScaledFont Created By Index";
-}
-
-inline void
-RecordedScaledFontCreationByIndex::SetFontInstanceData(const uint8_t *aData, uint32_t aSize,
-                                                const FontVariation* aVariations, uint32_t aNumVariations)
-{
-  mInstanceData.assign(aData, aData + aSize);
-  mVariations.assign(aVariations, aVariations + aNumVariations);
-}
-
-template<class S>
-RecordedScaledFontCreationByIndex::RecordedScaledFontCreationByIndex(S &aStream)
-  : RecordedEventDerived(SCALEDFONTCREATIONBYINDEX)
-{
-  ReadElement(aStream, mRefPtr);
-  ReadElement(aStream, mUnscaledFontIndex);
-  ReadElement(aStream, mGlyphSize);
-
-  size_t size;
-  ReadElement(aStream, size);
-  mInstanceData.resize(size);
-  aStream.read((char*)mInstanceData.data(), size);
-  size_t numVariations;
-  ReadElement(aStream, numVariations);
-  mVariations.resize(numVariations);
-  aStream.read((char*)mVariations.data(), sizeof(FontVariation) * numVariations);
-}
-
-inline bool
 RecordedScaledFontDestruction::PlayEvent(Translator *aTranslator) const
 {
   aTranslator->RemoveScaledFont(mRefPtr);
   return true;
 }
 
 template<class S>
 void
@@ -3558,17 +3450,16 @@ RecordedFilterNodeSetInput::OutputSimple
     f(SOURCESURFACECREATION, RecordedSourceSurfaceCreation); \
     f(SOURCESURFACEDESTRUCTION, RecordedSourceSurfaceDestruction); \
     f(FILTERNODECREATION, RecordedFilterNodeCreation); \
     f(FILTERNODEDESTRUCTION, RecordedFilterNodeDestruction); \
     f(GRADIENTSTOPSCREATION, RecordedGradientStopsCreation); \
     f(GRADIENTSTOPSDESTRUCTION, RecordedGradientStopsDestruction); \
     f(SNAPSHOT, RecordedSnapshot); \
     f(SCALEDFONTCREATION, RecordedScaledFontCreation); \
-    f(SCALEDFONTCREATIONBYINDEX, RecordedScaledFontCreationByIndex); \
     f(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction); \
     f(MASKSURFACE, RecordedMaskSurface); \
     f(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute); \
     f(FILTERNODESETINPUT, RecordedFilterNodeSetInput); \
     f(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget); \
     f(CREATECLIPPEDDRAWTARGET, RecordedCreateClippedDrawTarget); \
     f(FONTDATA, RecordedFontData); \
     f(FONTDESC, RecordedFontDescriptor); \
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -392,16 +392,37 @@ UnscaledFontDWrite::GetWRFontDescriptor(
   // the data payload.
   uint32_t index = weight | (stretch << 16) | (style << 24);
   aCb(reinterpret_cast<const uint8_t*>(familyName.data()),
       (familyName.size() - 1) * sizeof(WCHAR),
       index, aBaton);
   return true;
 }
 
+ScaledFontDWrite::InstanceData::InstanceData(const wr::FontInstanceOptions* aOptions,
+                                             const wr::FontInstancePlatformOptions* aPlatformOptions)
+  : mUseEmbeddedBitmap(false)
+  , mForceGDIMode(false)
+  , mGamma(2.2f)
+  , mContrast(1.0f)
+{
+  if (aOptions) {
+    if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) {
+      mUseEmbeddedBitmap = true;
+    }
+    if (aOptions->flags & wr::FontInstanceFlags::FORCE_GDI) {
+      mForceGDIMode = true;
+    }
+  }
+  if (aPlatformOptions) {
+    mGamma = aPlatformOptions->gamma / 100.0f;
+    mContrast = aPlatformOptions->contrast / 100.0f;
+  }
+}
+
 // Helper for ScaledFontDWrite::GetFontInstanceData: if the font has variation
 // axes, get their current values into the aOutput vector.
 static void
 GetVariationsFromFontFace(IDWriteFontFace* aFace,
                           std::vector<FontVariation>* aOutput)
 {
   RefPtr<IDWriteFontFace5> ff5;
   aFace->QueryInterface(__uuidof(IDWriteFontFace5), (void**)getter_AddRefs(ff5));
@@ -536,49 +557,63 @@ UnscaledFontDWrite::CreateScaledFont(Flo
                                      uint32_t aInstanceDataLength,
                                      const FontVariation* aVariations,
                                      uint32_t aNumVariations)
 {
   if (aInstanceDataLength < sizeof(ScaledFontDWrite::InstanceData)) {
     gfxWarning() << "DWrite scaled font instance data is truncated.";
     return nullptr;
   }
+  const ScaledFontDWrite::InstanceData& instanceData =
+    *reinterpret_cast<const ScaledFontDWrite::InstanceData*>(aInstanceData);
 
   IDWriteFontFace* face = mFontFace;
 
   // If variations are required, we create a separate IDWriteFontFace5 with
   // the requested settings applied.
   RefPtr<IDWriteFontFace5> ff5;
   if (aNumVariations) {
     ff5 = CreateFaceWithVariations(mFontFace, aVariations, aNumVariations);
     if (ff5) {
       face = ff5;
     } else {
       gfxWarning() << "Failed to create IDWriteFontFace5 with variations.";
     }
   }
 
-  const ScaledFontDWrite::InstanceData *instanceData =
-    reinterpret_cast<const ScaledFontDWrite::InstanceData*>(aInstanceData);
   RefPtr<ScaledFontBase> scaledFont =
     new ScaledFontDWrite(face, this, aGlyphSize,
-                         instanceData->mUseEmbeddedBitmap,
-                         instanceData->mForceGDIMode,
+                         instanceData.mUseEmbeddedBitmap,
+                         instanceData.mForceGDIMode,
                          nullptr,
-                         instanceData->mGamma,
-                         instanceData->mContrast);
+                         instanceData.mGamma,
+                         instanceData.mContrast);
 
   if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
     gfxWarning() << "Unable to create cairo scaled font DWrite font.";
     return nullptr;
   }
 
   return scaledFont.forget();
 }
 
+already_AddRefed<ScaledFont>
+UnscaledFontDWrite::CreateScaledFontFromWRFont(Float aGlyphSize,
+                                               const wr::FontInstanceOptions* aOptions,
+                                               const wr::FontInstancePlatformOptions* aPlatformOptions,
+                                               const FontVariation* aVariations,
+                                               uint32_t aNumVariations)
+{
+  ScaledFontDWrite::InstanceData instanceData(aOptions, aPlatformOptions);
+  return CreateScaledFont(aGlyphSize,
+                          reinterpret_cast<uint8_t*>(&instanceData),
+                          sizeof(instanceData),
+                          aVariations, aNumVariations);
+}
+
 AntialiasMode
 ScaledFontDWrite::GetDefaultAAMode()
 {
   AntialiasMode defaultMode = GetSystemDefaultAAMode();
 
   if (defaultMode == AntialiasMode::GRAY) {
     if (!DoGrayscale(mFontFace, mSize)) {
       defaultMode = AntialiasMode::NONE;
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -97,16 +97,19 @@ private:
   {
     explicit InstanceData(ScaledFontDWrite* aScaledFont)
       : mUseEmbeddedBitmap(aScaledFont->mUseEmbeddedBitmap)
       , mForceGDIMode(aScaledFont->mForceGDIMode)
       , mGamma(aScaledFont->mGamma)
       , mContrast(aScaledFont->mContrast)
     {}
 
+    InstanceData(const wr::FontInstanceOptions* aOptions,
+                 const wr::FontInstancePlatformOptions* aPlatformOptions);
+
     bool mUseEmbeddedBitmap;
     bool mForceGDIMode;
     Float mGamma;
     Float mContrast;
   };
 };
 
 }
--- a/gfx/2d/ScaledFontFontconfig.cpp
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -102,16 +102,79 @@ ScaledFontFontconfig::InstanceData::Inst
         hintstyle = FC_HINT_FULL;
       }
       mHintStyle = hintstyle;
     }
   }
   cairo_font_options_destroy(fontOptions);
 }
 
+ScaledFontFontconfig::InstanceData::InstanceData(const wr::FontInstanceOptions* aOptions,
+                                                 const wr::FontInstancePlatformOptions* aPlatformOptions)
+  : mFlags(HINT_METRICS)
+  , mHintStyle(FC_HINT_FULL)
+  , mSubpixelOrder(FC_RGBA_UNKNOWN)
+  , mLcdFilter(FC_LCD_LEGACY)
+{
+  if (aOptions) {
+    if (aOptions->flags & wr::FontInstanceFlags::FORCE_AUTOHINT) {
+      mFlags |= AUTOHINT;
+    }
+    if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) {
+      mFlags |= EMBEDDED_BITMAP;
+    }
+    if (aOptions->flags & wr::FontInstanceFlags::SYNTHETIC_BOLD) {
+      mFlags |= EMBOLDEN;
+    }
+    if (aOptions->flags & wr::FontInstanceFlags::VERTICAL_LAYOUT) {
+      mFlags |= VERTICAL_LAYOUT;
+    }
+    if (aOptions->render_mode != wr::FontRenderMode::Mono) {
+      mFlags |= ANTIALIAS;
+      if (aOptions->render_mode == wr::FontRenderMode::Subpixel) {
+        if (aOptions->flags & wr::FontInstanceFlags::SUBPIXEL_BGR) {
+          mSubpixelOrder =
+            aOptions->flags & wr::FontInstanceFlags::LCD_VERTICAL ? FC_RGBA_VBGR : FC_RGBA_BGR;
+        } else {
+          mSubpixelOrder =
+            aOptions->flags & wr::FontInstanceFlags::LCD_VERTICAL ? FC_RGBA_VRGB : FC_RGBA_RGB;
+        }
+      }
+    }
+  }
+  if (aPlatformOptions) {
+    switch (aPlatformOptions->hinting) {
+    case wr::FontHinting::None:
+      mHintStyle = FC_HINT_NONE;
+      break;
+    case wr::FontHinting::Light:
+      mHintStyle = FC_HINT_SLIGHT;
+      break;
+    case wr::FontHinting::Normal:
+      mHintStyle = FC_HINT_MEDIUM;
+      break;
+    default:
+      break;
+    }
+    switch (aPlatformOptions->lcd_filter) {
+    case wr::FontLCDFilter::None:
+      mLcdFilter = FC_LCD_NONE;
+      break;
+    case wr::FontLCDFilter::Default:
+      mLcdFilter = FC_LCD_DEFAULT;
+      break;
+    case wr::FontLCDFilter::Light:
+      mLcdFilter = FC_LCD_LIGHT;
+      break;
+    default:
+      break;
+    }
+  }
+}
+
 void
 ScaledFontFontconfig::InstanceData::SetupPattern(FcPattern* aPattern) const
 {
   if (mFlags & AUTOHINT) {
     FcPatternAddBool(aPattern, FC_AUTOHINT, FcTrue);
   }
   if (mFlags & EMBEDDED_BITMAP) {
     FcPatternAddBool(aPattern, FC_EMBEDDED_BITMAP, FcTrue);
@@ -348,37 +411,16 @@ ScaledFontFontconfig::GetWRFontInstanceO
     if (FcPatternGetFTFace(mPattern, FC_FT_FACE, 0, &face) == FcResultMatch) {
       UnscaledFontFreeType::GetVariationSettingsFromFace(aOutVariations, face);
     }
   }
 
   return true;
 }
 
-already_AddRefed<ScaledFont>
-UnscaledFontFontconfig::CreateScaledFont(Float aGlyphSize,
-                                         const uint8_t* aInstanceData,
-                                         uint32_t aInstanceDataLength,
-                                         const FontVariation* aVariations,
-                                         uint32_t aNumVariations)
-{
-  if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) {
-    gfxWarning() << "Fontconfig scaled font instance data is truncated.";
-    return nullptr;
-  }
-  const ScaledFontFontconfig::InstanceData *instanceData =
-    reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(aInstanceData);
-  RefPtr<ScaledFont> scaledFont =
-    ScaledFontFontconfig::CreateFromInstanceData(*instanceData, this, aGlyphSize,
-                                                 aVariations, aNumVariations,
-                                                 static_cast<NativeFontResourceFontconfig*>(
-                                                   mNativeFontResource.get()));
-  return scaledFont.forget();
-}
-
 static cairo_user_data_key_t sNativeFontResourceKey;
 
 static void
 ReleaseNativeFontResource(void* aData)
 {
   static_cast<NativeFontResource*>(aData)->Release();
 }
 
@@ -386,129 +428,150 @@ static cairo_user_data_key_t sFaceKey;
 
 static void
 ReleaseFace(void* aData)
 {
   Factory::ReleaseFTFace(static_cast<FT_Face>(aData));
 }
 
 already_AddRefed<ScaledFont>
-ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
-                                             UnscaledFontFontconfig* aUnscaledFont,
-                                             Float aSize,
-                                             const FontVariation* aVariations,
-                                             uint32_t aNumVariations,
-                                             NativeFontResourceFontconfig* aNativeFontResource)
+UnscaledFontFontconfig::CreateScaledFont(Float aSize,
+                                         const uint8_t* aInstanceData,
+                                         uint32_t aInstanceDataLength,
+                                         const FontVariation* aVariations,
+                                         uint32_t aNumVariations)
 {
+  if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) {
+    gfxWarning() << "Fontconfig scaled font instance data is truncated.";
+    return nullptr;
+  }
+  const ScaledFontFontconfig::InstanceData& instanceData =
+    *reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(aInstanceData);
+
   FcPattern* pattern = FcPatternCreate();
   if (!pattern) {
     gfxWarning() << "Failed initializing Fontconfig pattern for scaled font";
     return nullptr;
   }
-  FT_Face face = aUnscaledFont->GetFace();
+  FT_Face face = GetFace();
+  NativeFontResourceFreeType* nfr = static_cast<NativeFontResourceFreeType*>(mNativeFontResource.get());
   FT_Face varFace = nullptr;
   if (face) {
-    if (aNativeFontResource && aNumVariations > 0) {
-      varFace = aNativeFontResource->CloneFace();
+    if (nfr && aNumVariations > 0) {
+      varFace = nfr->CloneFace();
       if (!varFace) {
         gfxWarning() << "Failed cloning face for variations";
       }
     }
     FcPatternAddFTFace(pattern, FC_FT_FACE, varFace ? varFace : face);
   } else {
-    FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(aUnscaledFont->GetFile()));
-    FcPatternAddInteger(pattern, FC_INDEX, aUnscaledFont->GetIndex());
+    FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(GetFile()));
+    FcPatternAddInteger(pattern, FC_INDEX, GetIndex());
   }
   FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aSize);
-  aInstanceData.SetupPattern(pattern);
+  instanceData.SetupPattern(pattern);
 
   StackArray<FT_Fixed, 32> coords(aNumVariations);
   for (uint32_t i = 0; i < aNumVariations; i++) {
     coords[i] = std::round(aVariations[i].mValue * 65536.0);
   }
 
   cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern, coords.data(), aNumVariations);
   if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
     gfxWarning() << "Failed creating Cairo font face for Fontconfig pattern";
     FcPatternDestroy(pattern);
     if (varFace) {
       Factory::ReleaseFTFace(varFace);
     }
     return nullptr;
   }
 
-  if (aNativeFontResource) {
+  if (nfr) {
     // Bug 1362117 - Cairo may keep the font face alive after the owning NativeFontResource
     // was freed. To prevent this, we must bind the NativeFontResource to the font face so that
     // it stays alive at least as long as the font face.
-    aNativeFontResource->AddRef();
+    nfr->AddRef();
     cairo_status_t err = CAIRO_STATUS_SUCCESS;
     bool cleanupFace = false;
     if (varFace) {
       err = cairo_font_face_set_user_data(font,
                                           &sFaceKey,
                                           varFace,
                                           ReleaseFace);
     }
     if (err != CAIRO_STATUS_SUCCESS) {
       cleanupFace = true;
     } else {
       err = cairo_font_face_set_user_data(font,
                                           &sNativeFontResourceKey,
-                                          aNativeFontResource,
+                                          nfr,
                                           ReleaseNativeFontResource);
     }
     if (err != CAIRO_STATUS_SUCCESS) {
       gfxWarning() << "Failed binding NativeFontResource to Cairo font face";
       if (varFace && cleanupFace) {
         Factory::ReleaseFTFace(varFace);
       }
-      aNativeFontResource->Release();
+      nfr->Release();
       cairo_font_face_destroy(font);
       FcPatternDestroy(pattern);
       return nullptr;
     }
   }
 
   cairo_matrix_t sizeMatrix;
   cairo_matrix_init(&sizeMatrix, aSize, 0, 0, aSize, 0, 0);
 
   cairo_matrix_t identityMatrix;
   cairo_matrix_init_identity(&identityMatrix);
 
   cairo_font_options_t *fontOptions = cairo_font_options_create();
-  aInstanceData.SetupFontOptions(fontOptions);
+  instanceData.SetupFontOptions(fontOptions);
 
   cairo_scaled_font_t* cairoScaledFont =
     cairo_scaled_font_create(font, &sizeMatrix, &identityMatrix, fontOptions);
 
   cairo_font_options_destroy(fontOptions);
   cairo_font_face_destroy(font);
 
   if (cairo_scaled_font_status(cairoScaledFont) != CAIRO_STATUS_SUCCESS) {
     gfxWarning() << "Failed creating Cairo scaled font for font face";
     FcPatternDestroy(pattern);
     return nullptr;
   }
 
   RefPtr<ScaledFontFontconfig> scaledFont =
-    new ScaledFontFontconfig(cairoScaledFont, pattern, aUnscaledFont, aSize);
+    new ScaledFontFontconfig(cairoScaledFont, pattern, this, aSize);
 
   cairo_scaled_font_destroy(cairoScaledFont);
   FcPatternDestroy(pattern);
 
   // Only apply variations if we have an explicitly cloned face. Otherwise,
   // if the pattern holds the pathname, Cairo will handle setting of variations.
   if (varFace) {
-    UnscaledFontFreeType::ApplyVariationsToFace(aVariations, aNumVariations, varFace);
+    ApplyVariationsToFace(aVariations, aNumVariations, varFace);
   }
 
   return scaledFont.forget();
 }
 
+already_AddRefed<ScaledFont>
+UnscaledFontFontconfig::CreateScaledFontFromWRFont(Float aGlyphSize,
+                                                   const wr::FontInstanceOptions* aOptions,
+                                                   const wr::FontInstancePlatformOptions* aPlatformOptions,
+                                                   const FontVariation* aVariations,
+                                                   uint32_t aNumVariations)
+{
+  ScaledFontFontconfig::InstanceData instanceData(aOptions, aPlatformOptions);
+  return CreateScaledFont(aGlyphSize,
+                          reinterpret_cast<uint8_t*>(&instanceData),
+                          sizeof(instanceData),
+                          aVariations, aNumVariations);
+}
+
 bool
 ScaledFontFontconfig::HasVariationSettings()
 {
   // Check if the FT face has been cloned.
   FT_Face face = nullptr;
   return FcPatternGetFTFace(mPattern, FC_FT_FACE, 0, &face) == FcResultMatch &&
          face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS &&
          face != static_cast<UnscaledFontFontconfig*>(mUnscaledFont.get())->GetFace();
--- a/gfx/2d/ScaledFontFontconfig.h
+++ b/gfx/2d/ScaledFontFontconfig.h
@@ -52,33 +52,27 @@ private:
       AUTOHINT        = 1 << 1,
       EMBEDDED_BITMAP = 1 << 2,
       EMBOLDEN        = 1 << 3,
       VERTICAL_LAYOUT = 1 << 4,
       HINT_METRICS    = 1 << 5
     };
 
     InstanceData(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern);
+    InstanceData(const wr::FontInstanceOptions* aOptions,
+                 const wr::FontInstancePlatformOptions* aPlatformOptions);
 
     void SetupPattern(FcPattern* aPattern) const;
     void SetupFontOptions(cairo_font_options_t* aFontOptions) const;
 
     uint8_t mFlags;
     uint8_t mHintStyle;
     uint8_t mSubpixelOrder;
     uint8_t mLcdFilter;
   };
 
-  static already_AddRefed<ScaledFont>
-    CreateFromInstanceData(const InstanceData& aInstanceData,
-                           UnscaledFontFontconfig* aUnscaledFont,
-                           Float aSize,
-                           const FontVariation* aVariations,
-                           uint32_t aNumVariations,
-                           NativeFontResourceFontconfig* aNativeFontResource = nullptr);
-
   FcPattern* mPattern;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SCALEDFONTFONTCONFIG_H_ */
--- a/gfx/2d/UnscaledFontDWrite.h
+++ b/gfx/2d/UnscaledFontDWrite.h
@@ -9,16 +9,18 @@
 
 #include <dwrite.h>
 
 #include "2D.h"
 
 namespace mozilla {
 namespace gfx {
 
+class ScaledFontDWrite;
+
 class UnscaledFontDWrite final : public UnscaledFont
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontDWrite, override)
   UnscaledFontDWrite(const RefPtr<IDWriteFontFace>& aFontFace,
                      const RefPtr<IDWriteFont>& aFont,
                      DWRITE_FONT_SIMULATIONS aSimulations = DWRITE_FONT_SIMULATIONS_NONE,
                      bool aNeedsCairo = false)
@@ -38,16 +40,23 @@ public:
 
   already_AddRefed<ScaledFont>
     CreateScaledFont(Float aGlyphSize,
                      const uint8_t* aInstanceData,
                      uint32_t aInstanceDataLength,
                      const FontVariation* aVariations,
                      uint32_t aNumVariations) override;
 
+  already_AddRefed<ScaledFont>
+    CreateScaledFontFromWRFont(Float aGlyphSize,
+                               const wr::FontInstanceOptions* aOptions,
+                               const wr::FontInstancePlatformOptions* aPlatformOptions,
+                               const FontVariation* aVariations,
+                               uint32_t aNumVariations) override;
+
   bool GetWRFontDescriptor(WRFontDescriptorOutput aCb, void* aBaton) override;
 
 private:
   RefPtr<IDWriteFontFace> mFontFace;
   RefPtr<IDWriteFont> mFont;
   DWRITE_FONT_SIMULATIONS mSimulations;
   bool mNeedsCairo;
 };
--- a/gfx/2d/UnscaledFontFreeType.h
+++ b/gfx/2d/UnscaledFontFreeType.h
@@ -79,17 +79,16 @@ public:
 
 protected:
   FT_Face mFace;
   bool mOwnsFace;
   std::string mFile;
   uint32_t mIndex;
   RefPtr<NativeFontResource> mNativeFontResource;
 
-private:
   friend class ScaledFontFreeType;
   friend class ScaledFontFontconfig;
 
   static void
     GetVariationSettingsFromFace(std::vector<FontVariation>* aVariations,
                                  FT_Face aFace);
 
   static void
@@ -126,16 +125,23 @@ public:
     CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex);
 
   already_AddRefed<ScaledFont>
     CreateScaledFont(Float aGlyphSize,
                      const uint8_t* aInstanceData,
                      uint32_t aInstanceDataLength,
                      const FontVariation* aVariations,
                      uint32_t aNumVariations) override;
+
+  already_AddRefed<ScaledFont>
+    CreateScaledFontFromWRFont(Float aGlyphSize,
+                               const wr::FontInstanceOptions* aOptions,
+                               const wr::FontInstancePlatformOptions* aPlatformOptions,
+                               const FontVariation* aVariations,
+                               uint32_t aNumVariations) override;
 };
 #endif
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_ */
 
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -280,71 +280,82 @@ WebRenderBridgeChild::PushGlyphs(wr::Dis
                     aBackfaceVisible,
                     aColor,
                     key,
                     aGlyphs,
                     aGlyphOptions);
 }
 
 wr::FontInstanceKey
-WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
+WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont,
+                                              wr::IpcResourceUpdateQueue* aResources)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aScaledFont);
   MOZ_ASSERT(aScaledFont->CanSerialize());
 
   wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 };
   if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) {
     return instanceKey;
   }
 
-  wr::IpcResourceUpdateQueue resources(this);
+  Maybe<wr::IpcResourceUpdateQueue> resources =
+    aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
+  aResources = resources.ptrOr(aResources);
 
-  wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont());
+  wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont(), aResources);
   wr::FontKey nullKey = { wr::IdNamespace { 0 }, 0};
   if (fontKey == nullKey) {
     return instanceKey;
   }
 
   instanceKey = GetNextFontInstanceKey();
 
   Maybe<wr::FontInstanceOptions> options;
   Maybe<wr::FontInstancePlatformOptions> platformOptions;
   std::vector<FontVariation> variations;
   aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions, &variations);
 
-  resources.AddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(),
-                            options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
-                            Range<const FontVariation>(variations.data(), variations.size()));
-  UpdateResources(resources);
+  aResources->AddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(),
+                              options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
+                              Range<const FontVariation>(variations.data(), variations.size()));
+  if (resources.isSome()) {
+    UpdateResources(resources.ref());
+  }
 
   mFontInstanceKeys.Put(aScaledFont, instanceKey);
 
   return instanceKey;
 
 }
 
 wr::FontKey
-WebRenderBridgeChild::GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaled)
+WebRenderBridgeChild::GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaled,
+                                                wr::IpcResourceUpdateQueue* aResources)
 {
   MOZ_ASSERT(!mDestroyed);
 
   wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0};
   if (!mFontKeys.Get(aUnscaled, &fontKey)) {
-    wr::IpcResourceUpdateQueue resources(this);
-    FontFileDataSink sink = { &fontKey, this, &resources };
+    Maybe<wr::IpcResourceUpdateQueue> resources =
+      aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
+
+    FontFileDataSink sink = { &fontKey, this, resources.ptrOr(aResources) };
     // First try to retrieve a descriptor for the font, as this is much cheaper
     // to send over IPC than the full raw font data. If this is not possible, then
     // and only then fall back to getting the raw font file data. If that fails,
     // then the only thing left to do is signal failure by returning a null font key.
     if (!aUnscaled->GetWRFontDescriptor(WriteFontDescriptor, &sink) &&
         !aUnscaled->GetFontFileData(WriteFontFileData, &sink)) {
       return fontKey;
     }
-    UpdateResources(resources);
+
+    if (resources.isSome()) {
+      UpdateResources(resources.ref());
+    }
 
     mFontKeys.Put(aUnscaled, fontKey);
   }
 
   return fontKey;
 }
 
 void
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -138,19 +138,20 @@ public:
 
   void PushGlyphs(wr::DisplayListBuilder& aBuilder, Range<const wr::GlyphInstance> aGlyphs,
                   gfx::ScaledFont* aFont, const wr::ColorF& aColor,
                   const StackingContextHelper& aSc,
                   const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
                   bool aBackfaceVisible,
                   const wr::GlyphOptions* aGlyphOptions = nullptr);
 
-  wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
-  wr::FontKey GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaledFont);
-
+  wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont,
+                                              wr::IpcResourceUpdateQueue* aResources = nullptr);
+  wr::FontKey GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaledFont,
+                                        wr::IpcResourceUpdateQueue* aResources = nullptr);
   void RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResources);
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
 
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
   ipc::IShmemAllocator* GetShmemAllocator();
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -350,16 +350,17 @@ struct DIGroup
   LayoutDeviceRect mPaintRect;
   int32_t mAppUnitsPerDevPixel;
   gfx::Size mScale;
   FrameMetrics::ViewID mScrollId;
   LayerPoint mResidualOffset;
   LayerIntRect mLayerBounds;
   Maybe<wr::ImageKey> mKey;
   std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
+  std::vector<RefPtr<ScaledFont>> mFonts;
 
   DIGroup()
     : mAppUnitsPerDevPixel(0)
     , mScrollId(FrameMetrics::NULL_SCROLL_ID)
   {
   }
 
   void InvalidateRect(const IntRect& aRect)
@@ -380,16 +381,26 @@ struct DIGroup
     for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
       BlobItemData* data = iter.Get()->GetKey();
       GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
       iter.Remove();
       delete data;
     }
   }
 
+  void ClearImageKey(WebRenderLayerManager* aManager, bool aForce = false)
+  {
+    if (mKey) {
+      MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty());
+      aManager->AddImageKeyForDiscard(mKey.value());
+      mKey = Nothing();
+    }
+    mFonts.clear();
+  }
+
   static IntRect
   ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, LayerIntPoint aOffset)
   {
     // RoundedOut can convert empty rectangles to non-empty ones
     // so special case them here
     if (aBounds.IsEmpty()) {
       return IntRect();
     }
@@ -621,44 +632,46 @@ struct DIGroup
       if (mKey) {
         SetBlobImageVisibleArea(aResources, mKey.value(), bounds, mPaintRect);
         PushImage(aBuilder, bounds);
       }
       return;
     }
 
     gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
+    std::vector<RefPtr<ScaledFont>> fonts;
     RefPtr<WebRenderDrawEventRecorder> recorder =
       MakeAndAddRef<WebRenderDrawEventRecorder>(
-        [&](MemStream& aStream, std::vector<RefPtr<UnscaledFont>>& aUnscaledFonts) {
-          size_t count = aUnscaledFonts.size();
+        [&](MemStream& aStream, std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
+          size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
-          for (auto unscaled : aUnscaledFonts) {
-            wr::FontKey key = aWrManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
-            aStream.write((const char*)&key, sizeof(key));
+          for (auto& scaled : aScaledFonts) {
+            BlobFont font = {
+              aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
+              scaled
+            };
+            aStream.write((const char*)&font, sizeof(font));
           }
+          fonts = std::move(aScaledFonts);
         });
 
     RefPtr<gfx::DrawTarget> dummyDt =
       gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
 
     RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, dtSize);
     // Setup the gfxContext
     RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
     GP("ctx-offset %f %f\n", bounds.x, bounds.y);
     context->SetMatrix(Matrix::Scaling(mScale.width, mScale.height).PreTranslate(-bounds.x, -bounds.y));
 
     GP("mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
 
     bool empty = aStartItem == aEndItem;
     if (empty) {
-      if (mKey) {
-        aWrManager->AddImageKeyForDiscard(mKey.value());
-        mKey = Nothing();
-      }
+      ClearImageKey(aWrManager, true);
       return;
     }
 
     PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
 
     // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);?
     bool isOpaque = false;
 
@@ -682,16 +695,17 @@ struct DIGroup
       auto bottomRight = mInvalidRect.BottomRight();
       GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, dtSize.width, dtSize.height);
       MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width && bottomRight.y <= dtSize.height);
       GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
       if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
         return;
       }
     }
+    mFonts = std::move(fonts);
     mInvalidRect.SetEmpty();
     SetBlobImageVisibleArea(aResources, mKey.value(), mPaintRect, bounds);
     PushImage(aBuilder, bounds);
     GP("End EndGroup\n\n");
   }
 
   void PushImage(wr::DisplayListBuilder& aBuilder, const LayoutDeviceRect& bounds)
   {
@@ -1017,21 +1031,17 @@ Grouper::ConstructGroups(nsDisplayListBu
           groupData->mFollowingGroup.mAppUnitsPerDevPixel != currentGroup->mAppUnitsPerDevPixel ||
           groupData->mFollowingGroup.mResidualOffset != currentGroup->mResidualOffset) {
         if (groupData->mFollowingGroup.mAppUnitsPerDevPixel != currentGroup->mAppUnitsPerDevPixel) {
           GP("app unit change following: %d %d\n", groupData->mFollowingGroup.mAppUnitsPerDevPixel, currentGroup->mAppUnitsPerDevPixel);
         }
         // The group changed size
         GP("Inner group size change\n");
         groupData->mFollowingGroup.ClearItems();
-        if (groupData->mFollowingGroup.mKey) {
-          MOZ_RELEASE_ASSERT(groupData->mFollowingGroup.mInvalidRect.IsEmpty());
-          aCommandBuilder->mManager->AddImageKeyForDiscard(groupData->mFollowingGroup.mKey.value());
-          groupData->mFollowingGroup.mKey = Nothing();
-        }
+        groupData->mFollowingGroup.ClearImageKey(aCommandBuilder->mManager);
       }
       groupData->mFollowingGroup.mGroupBounds = currentGroup->mGroupBounds;
       groupData->mFollowingGroup.mAppUnitsPerDevPixel = currentGroup->mAppUnitsPerDevPixel;
       groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
       groupData->mFollowingGroup.mScale = currentGroup->mScale;
       groupData->mFollowingGroup.mResidualOffset = currentGroup->mResidualOffset;
       groupData->mFollowingGroup.mPaintRect = currentGroup->mPaintRect;
 
@@ -1158,21 +1168,17 @@ WebRenderCommandBuilder::DoGroupingForDi
     }
     // The bounds have changed so we need to discard the old image and add all
     // the commands again.
     auto p = group.mGroupBounds;
     auto q = groupBounds;
     GP("Bounds change: %d %d %d %d vs %d %d %d %d\n", p.x, p.y, p.width, p.height, q.x, q.y, q.width, q.height);
 
     group.ClearItems();
-    if (group.mKey) {
-      MOZ_RELEASE_ASSERT(group.mInvalidRect.IsEmpty());
-      mManager->AddImageKeyForDiscard(group.mKey.value());
-      group.mKey = Nothing();
-    }
+    group.ClearImageKey(mManager);
   }
 
   FrameMetrics::ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
   if (const ActiveScrolledRoot* asr = aWrappingItem->GetActiveScrolledRoot()) {
     scrollId = asr->GetViewId();
   }
 
   g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
@@ -1791,25 +1797,30 @@ WebRenderCommandBuilder::GenerateFallbac
     newGeometry = aItem->AllocateGeometry(aDisplayListBuilder);
     fallbackData->SetGeometry(std::move(newGeometry));
 
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
                                                       gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
     if (useBlobImage) {
       bool snapped;
       bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);
+      std::vector<RefPtr<ScaledFont>> fonts;
 
       RefPtr<WebRenderDrawEventRecorder> recorder =
-        MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
-          size_t count = aUnscaledFonts.size();
+        MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts) {
+          size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
-          for (auto unscaled : aUnscaledFonts) {
-            wr::FontKey key = mManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
-            aStream.write((const char*)&key, sizeof(key));
+          for (auto& scaled : aScaledFonts) {
+            BlobFont font = {
+              mManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
+              scaled
+            };
+            aStream.write((const char*)&font, sizeof(font));
           }
+          fonts = std::move(aScaledFonts);
         });
       RefPtr<gfx::DrawTarget> dummyDt =
         gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
       RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, dtSize.ToUnknownSize());
       if (!fallbackData->mBasicLayerManager) {
         fallbackData->mBasicLayerManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
       }
       bool isInvalidated = PaintItemByDrawTarget(aItem, dt, paintRect, offset, aDisplayListBuilder,
@@ -1821,16 +1832,17 @@ WebRenderCommandBuilder::GenerateFallbac
       if (isInvalidated) {
         Range<uint8_t> bytes((uint8_t *)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
         wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
         wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque);
         if (!aResources.AddBlobImage(key, descriptor, bytes)) {
           return nullptr;
         }
         fallbackData->SetKey(key);
+        fallbackData->SetFonts(fonts);
       } else {
         // If there is no invalidation region and we don't have a image key,
         // it means we don't need to push image for the item.
         if (!fallbackData->GetKey().isSome()) {
           return nullptr;
         }
       }
     } else {
@@ -1987,12 +1999,14 @@ WebRenderGroupData::WebRenderGroupData(W
 {
   MOZ_COUNT_CTOR(WebRenderGroupData);
 }
 
 WebRenderGroupData::~WebRenderGroupData()
 {
   MOZ_COUNT_DTOR(WebRenderGroupData);
   GP("Group data destruct\n");
+  mSubGroup.ClearImageKey(mWRManager, true);
+  mFollowingGroup.ClearImageKey(mWRManager, true);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderDrawEventRecorder.h
+++ b/gfx/layers/wr/WebRenderDrawEventRecorder.h
@@ -7,16 +7,22 @@
 #define MOZILLA_LAYERS_WEBRENDERDRAWTARGETRECORDER_H
 
 #include "mozilla/gfx/DrawEventRecorder.h"
 #include "mozilla/gfx/InlineTranslator.h"
 
 namespace mozilla {
 namespace layers {
 
+struct BlobFont
+{
+  wr::FontInstanceKey mFontInstanceKey;
+  gfx::ReferencePtr mScaledFontPtr;
+};
+
 class WebRenderDrawEventRecorder final : public gfx::DrawEventRecorderMemory
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(WebRenderDrawEventRecorder, final)
 
   explicit WebRenderDrawEventRecorder(const gfx::SerializeResourcesFn &aSerialize)
     : DrawEventRecorderMemory(aSerialize)
   { }
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -172,24 +172,26 @@ public:
   nsDisplayItemGeometry* GetGeometry() override;
   void SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry);
   nsRect GetBounds() { return mBounds; }
   void SetBounds(const nsRect& aRect) { mBounds = aRect; }
   void SetInvalid(bool aInvalid) { mInvalid = aInvalid; }
   void SetScale(gfx::Size aScale) { mScale = aScale; }
   gfx::Size GetScale() { return mScale; }
   bool IsInvalid() { return mInvalid; }
+  void SetFonts(const std::vector<RefPtr<gfx::ScaledFont>>& aFonts) { mFonts = aFonts; }
 
   RefPtr<BasicLayerManager> mBasicLayerManager;
   std::vector<RefPtr<gfx::SourceSurface>> mExternalSurfaces;
 protected:
   nsAutoPtr<nsDisplayItemGeometry> mGeometry;
   nsRect mBounds;
   bool mInvalid;
   gfx::Size mScale;
+  std::vector<RefPtr<gfx::ScaledFont>> mFonts;
 };
 
 class WebRenderAnimationData : public WebRenderUserData
 {
 public:
   WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
   virtual ~WebRenderAnimationData();
 
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -21,28 +21,29 @@
 #include "mozilla/gfx/UnscaledFontMac.h"
 #elif defined(XP_WIN)
 #include "mozilla/gfx/UnscaledFontDWrite.h"
 #else
 #include "mozilla/gfx/UnscaledFontFreeType.h"
 #endif
 
 namespace std {
-  template <>
-    struct hash<mozilla::wr::FontKey>{
-      public :
-        size_t operator()(const mozilla::wr::FontKey &key ) const
-        {
-          return hash<size_t>()(mozilla::wr::AsUint64(key));
-        }
-    };
+  template <> struct hash<mozilla::wr::FontKey> {
+    size_t operator()(const mozilla::wr::FontKey& key) const {
+      return hash<size_t>()(mozilla::wr::AsUint64(key));
+    }
+  };
+
+  template <> struct hash<mozilla::wr::FontInstanceKey> {
+    size_t operator()(const mozilla::wr::FontInstanceKey& key) const {
+      return hash<size_t>()(mozilla::wr::AsUint64(key));
+    }
+  };
 };
 
-
-
 namespace mozilla {
 
 using namespace gfx;
 
 namespace wr {
 
 struct FontTemplate {
   const uint8_t* mData;
@@ -60,18 +61,34 @@ struct FontTemplate {
 
   ~FontTemplate() {
     if (mVec) {
       wr_dec_ref_arc(mVec);
     }
   }
 };
 
+struct FontInstanceData {
+  WrFontKey mFontKey;
+  float mSize;
+  Maybe<FontInstanceOptions> mOptions;
+  Maybe<FontInstancePlatformOptions> mPlatformOptions;
+  UniquePtr<FontVariation[]> mVariations;
+  size_t mNumVariations;
+  RefPtr<ScaledFont> mScaledFont;
+
+  FontInstanceData()
+    : mSize(0)
+    , mNumVariations(0)
+  {}
+};
+
 StaticMutex sFontDataTableLock;
-std::unordered_map<FontKey, FontTemplate> sFontDataTable;
+std::unordered_map<WrFontKey, FontTemplate> sFontDataTable;
+std::unordered_map<WrFontInstanceKey, FontInstanceData> sBlobFontTable;
 
 // Fixed-size ring buffer logging font deletion events to aid debugging.
 static struct FontDeleteLog {
   static const size_t MAX_ENTRIES = 256;
 
   uint64_t mEntries[MAX_ENTRIES] = { 0 };
   size_t mNextEntry = 0;
 
@@ -113,24 +130,32 @@ static struct FontDeleteLog {
     return "unknown font";
   }
 } sFontDeleteLog;
 
 void
 ClearAllBlobImageResources() {
   StaticMutexAutoLock lock(sFontDataTableLock);
   sFontDeleteLog.AddAll();
+  sBlobFontTable.clear();
   sFontDataTable.clear();
 }
 
 extern "C" {
 void
 ClearBlobImageResources(WrIdNamespace aNamespace) {
   StaticMutexAutoLock lock(sFontDataTableLock);
   sFontDeleteLog.Add(aNamespace);
+  for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) {
+    if (i->first.mNamespace == aNamespace) {
+      i = sBlobFontTable.erase(i);
+    } else {
+      i++;
+    }
+  }
   for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
     if (i->first.mNamespace == aNamespace) {
       i = sFontDataTable.erase(i);
     } else {
       i++;
     }
   }
 }
@@ -170,25 +195,62 @@ void
 DeleteFontData(WrFontKey aKey) {
   StaticMutexAutoLock lock(sFontDataTableLock);
   sFontDeleteLog.Add(aKey);
   auto i = sFontDataTable.find(aKey);
   if (i != sFontDataTable.end()) {
     sFontDataTable.erase(i);
   }
 }
+
+void
+AddBlobFont(WrFontInstanceKey aInstanceKey,
+            WrFontKey aFontKey,
+            float aSize,
+            const FontInstanceOptions* aOptions,
+            const FontInstancePlatformOptions* aPlatformOptions,
+            const FontVariation* aVariations,
+            size_t aNumVariations)
+{
+  StaticMutexAutoLock lock(sFontDataTableLock);
+  auto i = sBlobFontTable.find(aInstanceKey);
+  if (i == sBlobFontTable.end()) {
+    FontInstanceData& font = sBlobFontTable[aInstanceKey];
+    font.mFontKey = aFontKey;
+    font.mSize = aSize;
+    if (aOptions) {
+      font.mOptions = Some(*aOptions);
+    }
+    if (aPlatformOptions) {
+      font.mPlatformOptions = Some(*aPlatformOptions);
+    }
+    if (aNumVariations) {
+      font.mVariations.reset(new FontVariation[aNumVariations]);
+      PodCopy(font.mVariations.get(), aVariations, aNumVariations);
+    }
+  }
 }
 
-RefPtr<UnscaledFont>
-GetUnscaledFont(Translator *aTranslator, wr::FontKey key) {
+void
+DeleteBlobFont(WrFontInstanceKey aKey)
+{
   StaticMutexAutoLock lock(sFontDataTableLock);
-  auto i = sFontDataTable.find(key);
+  auto i = sBlobFontTable.find(aKey);
+  if (i != sBlobFontTable.end()) {
+    sBlobFontTable.erase(i);
+  }
+}
+
+static RefPtr<UnscaledFont>
+GetUnscaledFont(Translator* aTranslator, WrFontKey aKey)
+{
+  auto i = sFontDataTable.find(aKey);
   if (i == sFontDataTable.end()) {
-    gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << key.mHandle
-                                                 << " because " << sFontDeleteLog.Find(key);
+    gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle
+                                                 << " because " << sFontDeleteLog.Find(aKey);
     return nullptr;
   }
   FontTemplate &data = i->second;
   if (data.mUnscaledFont) {
     return data.mUnscaledFont;
   }
   MOZ_ASSERT(data.mData);
   FontType type =
@@ -203,29 +265,59 @@ GetUnscaledFont(Translator *aTranslator,
 #endif
   // makes a copy of the data
   RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource((uint8_t*)data.mData, data.mSize,
                                                                               aTranslator->GetReferenceDrawTarget()->GetBackendType(),
                                                                               type,
                                                                               aTranslator->GetFontContext());
   RefPtr<UnscaledFont> unscaledFont;
   if (!fontResource) {
-    gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to create NativeFontResource for FontKey " << key.mHandle;
+    gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
   } else {
     // Instance data is only needed for GDI fonts which webrender does not
     // support.
     unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
     if (!unscaledFont) {
-      gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << key.mHandle;
+      gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
     }
   }
   data.mUnscaledFont = unscaledFont;
   return unscaledFont;
 }
 
+static RefPtr<ScaledFont>
+GetScaledFont(Translator* aTranslator, WrFontInstanceKey aKey)
+{
+  StaticMutexAutoLock lock(sFontDataTableLock);
+  auto i = sBlobFontTable.find(aKey);
+  if (i == sBlobFontTable.end()) {
+    gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to get ScaledFont entry for FontInstanceKey " << aKey.mHandle;
+    return nullptr;
+  }
+  FontInstanceData &data = i->second;
+  if (data.mScaledFont) {
+    return data.mScaledFont;
+  }
+  RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
+  if (!unscaled) {
+    return nullptr;
+  }
+  RefPtr<ScaledFont> scaled =
+    unscaled->CreateScaledFontFromWRFont(data.mSize,
+                                         data.mOptions.ptrOr(nullptr),
+                                         data.mPlatformOptions.ptrOr(nullptr),
+                                         data.mVariations.get(),
+                                         data.mNumVariations);
+  if (!scaled) {
+    gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to create ScaledFont for FontKey " << aKey.mHandle;
+  }
+  data.mScaledFont = scaled;
+  return data.mScaledFont;
+}
+
 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
                                 gfx::IntSize aSize,
                                 gfx::SurfaceFormat aFormat,
                                 const uint16_t *aTileSize,
                                 const mozilla::wr::TileOffset *aTileOffset,
                                 const mozilla::wr::DeviceUintRect *aDirtyRect,
                                 Range<uint8_t> aOutput)
 {
@@ -324,20 +416,22 @@ static bool Moz2DRenderCallback(const Ra
       offset = extra_end;
       continue;
     }
 
     layers::WebRenderTranslator translator(dt);
 
     size_t count = *(size_t*)(aBlob.begin().get() + end);
     for (size_t i = 0; i < count; i++) {
-      wr::FontKey key = *(wr::FontKey*)(aBlob.begin() + end + sizeof(count) + sizeof(wr::FontKey)*i).get();
-      RefPtr<UnscaledFont> font = GetUnscaledFont(&translator, key);
-      translator.AddUnscaledFont(0, font);
+      layers::BlobFont blobFont =
+        *(layers::BlobFont*)(aBlob.begin() + end + sizeof(count) + sizeof(layers::BlobFont)*i).get();
+      RefPtr<ScaledFont> scaledFont = GetScaledFont(&translator, blobFont.mFontInstanceKey);
+      translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
     }
+
     Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
     ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length());
     MOZ_RELEASE_ASSERT(ret);
     offset = extra_end;
   }
 
 #if 0
   dt->SetTransform(gfx::Matrix());
@@ -356,21 +450,16 @@ static bool Moz2DRenderCallback(const Ra
   char filename[40];
   sprintf(filename, "out%d.png", i++);
   gfxUtils::WriteAsPNG(dt, filename);
 #endif
 
   return ret;
 }
 
-} // namespace
-} // namespace
-
-extern "C" {
-
 bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
                         uint32_t width, uint32_t height,
                         mozilla::wr::ImageFormat aFormat,
                         const uint16_t *aTileSize,
                         const mozilla::wr::TileOffset *aTileOffset,
                         const mozilla::wr::DeviceUintRect *aDirtyRect,
                         mozilla::wr::MutByteSlice output)
 {
@@ -380,9 +469,11 @@ bool wr_moz2d_render_cb(const mozilla::w
                                           aTileSize,
                                           aTileOffset,
                                           aDirtyRect,
                                           mozilla::wr::MutByteSliceToRange(output));
 }
 
 } // extern
 
+} // namespace
+} // namespace
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -68,17 +68,17 @@ pub type WrIdNamespace = IdNamespace;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrPipelineId = PipelineId;
 /// cbindgen:field-names=[mNamespace, mHandle]
 /// cbindgen:derive-neq=true
 type WrImageKey = ImageKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 pub type WrFontKey = FontKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
-type WrFontInstanceKey = FontInstanceKey;
+pub type WrFontInstanceKey = FontInstanceKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrYuvColorSpace = YuvColorSpace;
 
 fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
     if ptr.is_null() {
         &[]
     } else {
         unsafe { slice::from_raw_parts(ptr, len) }
--- a/gfx/webrender_bindings/src/moz2d_renderer.rs
+++ b/gfx/webrender_bindings/src/moz2d_renderer.rs
@@ -77,17 +77,17 @@ impl<'a> BufReader<'a> {
     }
 
     fn read<T>(&mut self) -> T {
         let ret = convert_from_bytes(&self.buf[self.pos..]);
         self.pos += mem::size_of::<T>();
         ret
     }
 
-    fn read_font_key(&mut self) -> FontKey {
+    fn read_blob_font(&mut self) -> BlobFont {
         self.read()
     }
 
     fn read_usize(&mut self) -> usize {
         self.read()
     }
 
     fn has_more(&self) -> bool {
@@ -328,16 +328,21 @@ fn merge_blob_images(old_buf: &[u8], new
 
     assert!(old_reader.cache.is_empty());
 
     let result = result.finish();
     check_result(&result);
     result
 }
 
+#[repr(C)]
+struct BlobFont {
+    font_instance_key: FontInstanceKey,
+    scaled_font_ptr: u64,
+}
 
 struct Moz2dBlobRasterizer {
     workers: Arc<ThreadPool>,
     blob_commands: HashMap<ImageKey, (Arc<BlobImageData>, Option<TileSize>)>,
 }
 
 impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
 
@@ -432,17 +437,18 @@ impl BlobImageHandler for Moz2dBlobImage
             blob_commands: self.blob_commands.clone(),
         })
     }
 
     fn delete_font(&mut self, font: FontKey) {
         unsafe { DeleteFontData(font); }
     }
 
-    fn delete_font_instance(&mut self, _key: FontInstanceKey) {
+    fn delete_font_instance(&mut self, key: FontInstanceKey) {
+        unsafe { DeleteBlobFont(key); }
     }
 
     fn clear_namespace(&mut self, namespace: IdNamespace) {
         unsafe { ClearBlobImageResources(namespace); }
     }
 
     fn prepare_resources(
         &mut self,
@@ -452,23 +458,33 @@ impl BlobImageHandler for Moz2dBlobImage
         for params in requests {
             let commands = &self.blob_commands[&params.request.key];
             let blob = Arc::clone(&commands.0);
             self.prepare_request(&blob, resources);
         }
     }
 }
 
-use bindings::{WrFontKey, WrIdNamespace};
+use bindings::{WrFontKey, WrFontInstanceKey, WrIdNamespace};
 
 #[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &Arc<Vec> to an extern function
 extern "C" {
     fn AddFontData(key: WrFontKey, data: *const u8, size: usize, index: u32, vec: &ArcVecU8);
     fn AddNativeFontHandle(key: WrFontKey, handle: *mut c_void, index: u32);
     fn DeleteFontData(key: WrFontKey);
+    fn AddBlobFont(
+        instance_key: WrFontInstanceKey,
+        font_key: WrFontKey,
+        size: f32,
+        options: *const FontInstanceOptions,
+        platform_options: *const FontInstancePlatformOptions,
+        variations: *const FontVariation,
+        num_variations: usize,
+    );
+    fn DeleteBlobFont(key: WrFontInstanceKey);
     fn ClearBlobImageResources(namespace: WrIdNamespace);
 }
 
 impl Moz2dBlobImageHandler {
     pub fn new(workers: Arc<ThreadPool>) -> Self {
         Moz2dBlobImageHandler {
             blob_commands: HashMap::new(),
             workers: workers,
@@ -490,34 +506,66 @@ impl Moz2dBlobImageHandler {
         }
 
         #[cfg(not(any(target_os = "macos", target_os = "windows")))]
         fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
             let cstr = CString::new(handle.pathname.clone()).unwrap();
             unsafe { AddNativeFontHandle(key, cstr.as_ptr() as *mut c_void, handle.index) };
         }
 
-        fn process_fonts(mut extra_data: BufReader, resources: &BlobImageResources) {
+        fn process_fonts(
+            mut extra_data: BufReader,
+            resources: &BlobImageResources,
+            unscaled_fonts: &mut Vec<FontKey>,
+            scaled_fonts: &mut Vec<FontInstanceKey>,
+        ) {
             let font_count = extra_data.read_usize();
             for _ in 0..font_count {
-                let key = extra_data.read_font_key();
-                let template = resources.get_font_data(key);
-                match template {
-                    &FontTemplate::Raw(ref data, ref index) => {
-                        unsafe { AddFontData(key, data.as_ptr(), data.len(), *index, data); }
+                let font = extra_data.read_blob_font();
+                if scaled_fonts.contains(&font.font_instance_key) {
+                    continue;
+                }
+                scaled_fonts.push(font.font_instance_key);
+                if let Some(instance) = resources.get_font_instance_data(font.font_instance_key) {
+                    if !unscaled_fonts.contains(&instance.font_key) {
+                        unscaled_fonts.push(instance.font_key);
+                        let template = resources.get_font_data(instance.font_key);
+                        match template {
+                            &FontTemplate::Raw(ref data, ref index) => {
+                                unsafe { AddFontData(instance.font_key, data.as_ptr(), data.len(), *index, data); }
+                            }
+                            &FontTemplate::Native(ref handle) => {
+                                process_native_font_handle(instance.font_key, handle);
+                            }
+                        }
                     }
-                    &FontTemplate::Native(ref handle) => {
-                        process_native_font_handle(key, handle);
+                    unsafe {
+                        AddBlobFont(
+                            font.font_instance_key,
+                            instance.font_key,
+                            instance.size.to_f32_px(),
+                            option_to_nullable(&instance.options),
+                            option_to_nullable(&instance.platform_options),
+                            instance.variations.as_ptr(),
+                            instance.variations.len(),
+                        );
                     }
                 }
-                resources.get_font_data(key);
             }
         }
+
         {
             let mut index = BlobReader::new(blob);
+            let mut unscaled_fonts = Vec::new();
+            let mut scaled_fonts = Vec::new();
             while index.reader.pos < index.reader.buf.len() {
                 let e  = index.read_entry();
-                process_fonts(BufReader::new(&blob[e.end..e.extra_end]), resources);
+                process_fonts(
+                    BufReader::new(&blob[e.end..e.extra_end]),
+                    resources,
+                    &mut unscaled_fonts,
+                    &mut scaled_fonts,
+                );
             }
         }
     }
 }
 
--- a/gfx/webrender_bindings/webrender_ffi.h
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -46,31 +46,33 @@ struct FontInstanceFlags {
     return *this;
   }
 
   FontInstanceFlags& operator|=(uint32_t aBits) {
     bits |= aBits;
     return *this;
   }
 
-  FontInstanceFlags operator|(uint32_t aBits) {
+  FontInstanceFlags operator|(uint32_t aBits) const {
     FontInstanceFlags flags = { bits | aBits };
     return flags;
   }
 
   FontInstanceFlags& operator&=(uint32_t aBits) {
     bits &= aBits;
     return *this;
   }
 
-  FontInstanceFlags operator&(uint32_t aBits) {
+  FontInstanceFlags operator&(uint32_t aBits) const {
     FontInstanceFlags flags = { bits & aBits };
     return flags;
   }
 
+  MOZ_IMPLICIT operator bool() const { return bits != 0; }
+
   enum : uint32_t {
     SYNTHETIC_BOLD    = 1 << 1,
     EMBEDDED_BITMAPS  = 1 << 2,
     SUBPIXEL_BGR      = 1 << 3,
     TRANSPOSE         = 1 << 4,
     FLIP_X            = 1 << 5,
     FLIP_Y            = 1 << 6,
     SUBPIXEL_POSITION = 1 << 7,