Bug 1348980 - implement UnscaledFont API for Moz2D and thebes. r=jfkthame
authorLee Salzman <lsalzman@mozilla.com>
Thu, 06 Apr 2017 17:41:02 -0400
changeset 351765 42dc8787a005b5fc95bd6bf19c49497ec6025a50
parent 351764 ac8fcfa12ddbd126ae57620db7bcf2a8657c49eb
child 351766 b475e054bbd1e20d0f8e29f2e292d927b4981838
push id31621
push userarchaeopteryx@coole-files.de
push dateFri, 07 Apr 2017 20:14:04 +0000
treeherdermozilla-central@35c7be9c2db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1348980
milestone55.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 1348980 - implement UnscaledFont API for Moz2D and thebes. r=jfkthame
gfx/2d/2D.h
gfx/2d/Factory.cpp
gfx/2d/NativeFontResourceDWrite.cpp
gfx/2d/NativeFontResourceGDI.cpp
gfx/2d/NativeFontResourceMac.cpp
gfx/2d/ScaledFontBase.cpp
gfx/2d/ScaledFontBase.h
gfx/2d/ScaledFontCairo.cpp
gfx/2d/ScaledFontCairo.h
gfx/2d/ScaledFontDWrite.cpp
gfx/2d/ScaledFontDWrite.h
gfx/2d/ScaledFontFontconfig.cpp
gfx/2d/ScaledFontFontconfig.h
gfx/2d/ScaledFontMac.cpp
gfx/2d/ScaledFontMac.h
gfx/2d/ScaledFontWin.cpp
gfx/2d/ScaledFontWin.h
gfx/2d/Types.h
gfx/2d/UnscaledFontDWrite.h
gfx/2d/UnscaledFontFreeType.h
gfx/2d/UnscaledFontGDI.h
gfx/2d/UnscaledFontMac.h
gfx/2d/moz.build
gfx/thebes/gfxAndroidPlatform.h
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxDWriteFonts.h
gfx/thebes/gfxFT2FontBase.cpp
gfx/thebes/gfxFT2FontBase.h
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFT2FontList.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFT2Fonts.h
gfx/thebes/gfxFcPlatformFontList.cpp
gfx/thebes/gfxFcPlatformFontList.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontEntry.h
gfx/thebes/gfxFontconfigUtils.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxGDIFontList.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxMacFont.h
gfx/thebes/gfxMacPlatformFontList.h
gfx/thebes/gfxMacPlatformFontList.mm
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatformGtk.cpp
gfx/thebes/gfxWindowsPlatform.cpp
layout/generic/nsTextFrame.cpp
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -20,16 +20,17 @@
 // to be able to hold on to a GLContext.
 #include "mozilla/GenericRefCounted.h"
 #include "mozilla/MemoryReporting.h"
 
 // This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
 // outparams using the &-operator. But it will have to do as there's no easy
 // solution.
 #include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
 
 #include "mozilla/DebugOnly.h"
 
 #ifdef MOZ_ENABLE_FREETYPE
 #include <string>
 #endif
 
 struct _cairo_surface;
@@ -691,16 +692,37 @@ struct GlyphMetrics
   // glyph, this is used when drawing vertically and will typically be 0.
   Float mYAdvance;
   // Width of the glyph's black box.
   Float mWidth;
   // Height of the glyph's black box.
   Float mHeight;
 };
 
+class UnscaledFont
+  : public external::AtomicRefCounted<UnscaledFont>
+  , public SupportsWeakPtr<UnscaledFont>
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFont)
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(UnscaledFont)
+
+  virtual ~UnscaledFont();
+
+  virtual FontType GetType() const = 0;
+
+  static uint32_t DeletionCounter() { return sDeletionCounter; }
+
+protected:
+  UnscaledFont() {}
+
+private:
+  static uint32_t sDeletionCounter;
+};
+
 /** This class is an abstraction of a backend/platform specific font object
  * at a particular size. It is passed into text drawing calls to describe
  * the font used for the drawing call.
  */
 class ScaledFont : public external::AtomicRefCounted<ScaledFont>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)
@@ -713,16 +735,17 @@ public:
 
   typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize,
                                      uint32_t aVariationCount, const VariationSetting* aVariations,
                                      void *aBaton);
   typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength, void* aBaton);
   typedef void (*FontDescriptorOutput)(const uint8_t* aData, uint32_t aLength, Float aFontSize, void* aBaton);
 
   virtual FontType GetType() const = 0;
+  virtual Float GetSize() const = 0;
   virtual AntialiasMode GetDefaultAAMode();
 
   /** This allows getting a path that describes the outline of a set of glyphs.
    * A target is passed in so that the guarantee is made the returned path
    * can be used with any DrawTarget that has the same backend as the one
    * passed in.
    */
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) = 0;
@@ -748,20 +771,25 @@ public:
 
   void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
     mUserData.Add(key, userData, destroy);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
   }
 
+  const RefPtr<UnscaledFont>& GetUnscaledFont() const { return mUnscaledFont; }
+
 protected:
-  ScaledFont() {}
+  explicit ScaledFont(const RefPtr<UnscaledFont>& aUnscaledFont)
+    : mUnscaledFont(aUnscaledFont)
+  {}
 
   UserData mUserData;
+  RefPtr<UnscaledFont> mUnscaledFont;
 };
 
 /**
  * Derived classes hold a native font resource from which to create
  * ScaledFonts.
  */
 class NativeFontResource : public RefCounted<NativeFontResource>
 {
@@ -1421,21 +1449,24 @@ public:
 
   static already_AddRefed<DrawTarget>
     CreateRecordingDrawTarget(DrawEventRecorder *aRecorder, DrawTarget *aDT);
 
   static already_AddRefed<DrawTarget>
     CreateDrawTargetForData(BackendType aBackend, unsigned char* aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);
 
   static already_AddRefed<ScaledFont>
-    CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
+    CreateScaledFontForNativeFont(const NativeFont &aNativeFont,
+                                  const RefPtr<UnscaledFont>& aUnscaledFont,
+                                  Float aSize);
 
 #ifdef MOZ_WIDGET_GTK
   static already_AddRefed<ScaledFont>
-    CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
+    CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
+                                      const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
 #endif
 
   /**
    * This creates a NativeFontResource from TrueType data.
    *
    * @param aData Pointer to the data
    * @param aSize Size of the TrueType data
    * @param aVariationCount Number of VariationSetting records provided.
@@ -1457,17 +1488,20 @@ public:
     CreateScaledFontFromFontDescriptor(FontType aType, const uint8_t* aData, uint32_t aDataLength, Float aSize);
 
   /**
    * This creates a scaled font with an associated cairo_scaled_font_t, and
    * must be used when using the Cairo backend. The NativeFont and
    * cairo_scaled_font_t* parameters must correspond to the same font.
    */
   static already_AddRefed<ScaledFont>
-    CreateScaledFontWithCairo(const NativeFont &aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont);
+    CreateScaledFontWithCairo(const NativeFont &aNativeFont,
+                              const RefPtr<UnscaledFont>& aUnscaledFont,
+                              Float aSize,
+                              cairo_scaled_font_t* aScaledFont);
 
   /**
    * This creates a simple data source surface for a certain size. It allocates
    * new memory for the surface. This memory is freed when the surface is
    * destroyed.  The caller is responsible for handing the case where nullptr
    * is returned. The surface is not zeroed unless requested.
    */
   static already_AddRefed<DataSourceSurface>
@@ -1583,16 +1617,17 @@ public:
 
   static uint64_t GetD2DVRAMUsageDrawTarget();
   static uint64_t GetD2DVRAMUsageSourceSurface();
   static void D2DCleanup();
 
   static already_AddRefed<ScaledFont>
     CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
                                   const gfxFontStyle* aStyle,
+                                  const RefPtr<UnscaledFont>& aUnscaledFont,
                                   Float aSize,
                                   bool aUseEmbeddedBitmap,
                                   bool aForceGDIMode);
 
   static void UpdateSystemTextQuality();
 
 private:
   static ID2D1Device *mD2D1Device;
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -464,41 +464,43 @@ Factory::GetMaxSurfaceSize(BackendType a
     return DrawTargetD2D1::GetMaxSurfaceSize();
 #endif
   default:
     return 0;
   }
 }
 
 already_AddRefed<ScaledFont>
-Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
+Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont,
+                                       const RefPtr<UnscaledFont>& aUnscaledFont,
+                                       Float aSize)
 {
   switch (aNativeFont.mType) {
 #ifdef WIN32
   case NativeFontType::DWRITE_FONT_FACE:
     {
-      return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
+      return MakeAndAddRef<ScaledFontDWrite>(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aUnscaledFont, aSize);
     }
 #if defined(USE_CAIRO) || defined(USE_SKIA)
   case NativeFontType::GDI_FONT_FACE:
     {
-      return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aSize);
+      return MakeAndAddRef<ScaledFontWin>(static_cast<LOGFONT*>(aNativeFont.mFont), aUnscaledFont, aSize);
     }
 #endif
 #endif
 #ifdef XP_DARWIN
   case NativeFontType::MAC_FONT_FACE:
     {
-      return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aSize);
+      return MakeAndAddRef<ScaledFontMac>(static_cast<CGFontRef>(aNativeFont.mFont), aUnscaledFont, aSize);
     }
 #endif
 #if defined(USE_CAIRO) || defined(USE_SKIA_FREETYPE)
   case NativeFontType::CAIRO_FONT_FACE:
     {
-      return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aSize);
+      return MakeAndAddRef<ScaledFontCairo>(static_cast<cairo_scaled_font_t*>(aNativeFont.mFont), aUnscaledFont, aSize);
     }
 #endif
   default:
     gfxWarning() << "Invalid native font type specified.";
     return nullptr;
   }
 }
 
@@ -559,36 +561,40 @@ Factory::CreateScaledFontFromFontDescrip
 #endif
   default:
     gfxWarning() << "Invalid type specified for ScaledFont font descriptor";
     return nullptr;
   }
 }
 
 already_AddRefed<ScaledFont>
-Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, cairo_scaled_font_t* aScaledFont)
+Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont,
+                                   const RefPtr<UnscaledFont>& aUnscaledFont,
+                                   Float aSize,
+                                   cairo_scaled_font_t* aScaledFont)
 {
 #ifdef USE_CAIRO
   // In theory, we could pull the NativeFont out of the cairo_scaled_font_t*,
   // but that would require a lot of code that would be otherwise repeated in
   // various backends.
   // Therefore, we just reuse CreateScaledFontForNativeFont's implementation.
-  RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aSize);
+  RefPtr<ScaledFont> font = CreateScaledFontForNativeFont(aNativeFont, aUnscaledFont, aSize);
   static_cast<ScaledFontBase*>(font.get())->SetCairoScaledFont(aScaledFont);
   return font.forget();
 #else
   return nullptr;
 #endif
 }
 
 #ifdef MOZ_WIDGET_GTK
 already_AddRefed<ScaledFont>
-Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
+Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
+                                           const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize)
 {
-  return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aSize);
+  return MakeAndAddRef<ScaledFontFontconfig>(aScaledFont, aPattern, aUnscaledFont, aSize);
 }
 #endif
 
 already_AddRefed<DrawTarget>
 Factory::CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB)
 {
   MOZ_ASSERT(targetA && targetB);
 
@@ -740,21 +746,22 @@ Factory::D2DCleanup()
     mD2D1Device = nullptr;
   }
   DrawTargetD2D1::CleanupD2D();
 }
 
 already_AddRefed<ScaledFont>
 Factory::CreateScaledFontForDWriteFont(IDWriteFontFace* aFontFace,
                                        const gfxFontStyle* aStyle,
+                                       const RefPtr<UnscaledFont>& aUnscaledFont,
                                        float aSize,
                                        bool aUseEmbeddedBitmap,
                                        bool aForceGDIMode)
 {
-  return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aSize,
+  return MakeAndAddRef<ScaledFontDWrite>(aFontFace, aUnscaledFont, aSize,
                                          aUseEmbeddedBitmap, aForceGDIMode,
                                          aStyle);
 }
 
 #endif // XP_WIN
 
 #ifdef USE_SKIA_GPU
 already_AddRefed<DrawTarget>
--- a/gfx/2d/NativeFontResourceDWrite.cpp
+++ b/gfx/2d/NativeFontResourceDWrite.cpp
@@ -270,17 +270,17 @@ NativeFontResourceDWrite::CreateScaledFo
   IDWriteFontFile *fontFile = mFontFile;
   RefPtr<IDWriteFontFace> fontFace;
   if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex,
     DWRITE_FONT_SIMULATIONS_NONE, getter_AddRefs(fontFace)))) {
     gfxWarning() << "Failed to create font face from font file data.";
     return nullptr;
   }
 
-  RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, aGlyphSize);
+  RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(fontFace, nullptr, aGlyphSize);
   if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
     gfxWarning() << "Unable to create cairo scaled font DWrite font.";
     return nullptr;
   }
 
   return scaledFont.forget();
 }
 
--- a/gfx/2d/NativeFontResourceGDI.cpp
+++ b/gfx/2d/NativeFontResourceGDI.cpp
@@ -45,17 +45,17 @@ NativeFontResourceGDI::CreateScaledFont(
     gfxWarning() << "GDI scaled font instance data is truncated.";
     return nullptr;
   }
 
   const LOGFONT* logFont = reinterpret_cast<const LOGFONT*>(aInstanceData);
 
   // Constructor for ScaledFontWin dereferences and copies the LOGFONT, so we
   // are safe to pass this reference.
-  RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, aGlyphSize);
+  RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, nullptr, aGlyphSize);
   if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
     gfxWarning() << "Unable to create cairo scaled font GDI font.";
     return nullptr;
   }
 
   return scaledFont.forget();
 }
 
--- a/gfx/2d/NativeFontResourceMac.cpp
+++ b/gfx/2d/NativeFontResourceMac.cpp
@@ -204,17 +204,17 @@ NativeFontResourceMac::Create(uint8_t *a
 
   return fontResource.forget();
 }
 
 already_AddRefed<ScaledFont>
 NativeFontResourceMac::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
                                         const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
 {
-  RefPtr<ScaledFontBase> scaledFont = new ScaledFontMac(mFontRef, aGlyphSize);
+  RefPtr<ScaledFontBase> scaledFont = new ScaledFontMac(mFontRef, nullptr, aGlyphSize);
 
   if (!scaledFont->PopulateCairoScaledFont()) {
     gfxWarning() << "Unable to create cairo scaled Mac font.";
     return nullptr;
   }
 
   return scaledFont.forget();
 }
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -21,16 +21,23 @@
 #include <vector>
 #include <cmath>
 
 using namespace std;
 
 namespace mozilla {
 namespace gfx {
 
+uint32_t UnscaledFont::sDeletionCounter = 0;
+
+UnscaledFont::~UnscaledFont()
+{
+  sDeletionCounter++;
+}
+
 AntialiasMode
 ScaledFont::GetDefaultAAMode()
 {
   if (gfxPrefs::DisableAllTextAA()) {
     return AntialiasMode::NONE;
   }
 
   return AntialiasMode::DEFAULT;
@@ -41,18 +48,20 @@ ScaledFontBase::~ScaledFontBase()
 #ifdef USE_SKIA
   SkSafeUnref(mTypeface);
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_scaled_font_destroy(mScaledFont);
 #endif
 }
 
-ScaledFontBase::ScaledFontBase(Float aSize)
-  : mSize(aSize)
+ScaledFontBase::ScaledFontBase(const RefPtr<UnscaledFont>& aUnscaledFont,
+                               Float aSize)
+  : ScaledFont(aUnscaledFont)
+  , mSize(aSize)
 {
 #ifdef USE_SKIA
   mTypeface = nullptr;
 #endif
 #ifdef USE_CAIRO_SCALED_FONT
   mScaledFont = nullptr;
 #endif
 }
--- a/gfx/2d/ScaledFontBase.h
+++ b/gfx/2d/ScaledFontBase.h
@@ -23,26 +23,26 @@
 
 namespace mozilla {
 namespace gfx {
 
 class ScaledFontBase : public ScaledFont
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontBase)
-  explicit ScaledFontBase(Float aSize);
+  ScaledFontBase(const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
   virtual ~ScaledFontBase();
 
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
 
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
 
-  float GetSize() { return mSize; }
+  virtual Float GetSize() const { return mSize; }
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface() { return mTypeface; }
 #endif
 
   // Not true, but required to instantiate a ScaledFontBase.
   virtual FontType GetType() const { return FontType::SKIA; }
 
--- a/gfx/2d/ScaledFontCairo.cpp
+++ b/gfx/2d/ScaledFontCairo.cpp
@@ -12,19 +12,21 @@
 
 namespace mozilla {
 namespace gfx {
 
 // On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
 // an SkFontHost implementation that allows Skia to render using this.
 // This is mainly because FT_Face is not good for sharing between libraries, which
 // is a requirement when we consider runtime switchable backends and so on
-ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize)
-  : ScaledFontBase(aSize)
-{ 
+ScaledFontCairo::ScaledFontCairo(cairo_scaled_font_t* aScaledFont,
+                                 const RefPtr<UnscaledFont>& aUnscaledFont,
+                                 Float aSize)
+  : ScaledFontBase(aUnscaledFont, aSize)
+{
   SetCairoScaledFont(aScaledFont);
 }
 
 #if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
 SkTypeface* ScaledFontCairo::GetSkTypeface()
 {
   if (!mTypeface) {
     mTypeface = SkCreateTypefaceFromCairoFTFont(mScaledFont);
--- a/gfx/2d/ScaledFontCairo.h
+++ b/gfx/2d/ScaledFontCairo.h
@@ -13,17 +13,19 @@
 namespace mozilla {
 namespace gfx {
 
 class ScaledFontCairo : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontCairo)
 
-  ScaledFontCairo(cairo_scaled_font_t* aScaledFont, Float aSize);
+  ScaledFontCairo(cairo_scaled_font_t* aScaledFont,
+                  const RefPtr<UnscaledFont>& aUnscaledFont,
+                  Float aSize);
 
 #if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
   virtual SkTypeface* GetSkTypeface();
 #endif
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -97,20 +97,23 @@ DWriteFontStretchFromStretch(int16_t aSt
             return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
         case NS_FONT_STRETCH_ULTRA_EXPANDED:
             return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
         default:
             return DWRITE_FONT_STRETCH_UNDEFINED;
     }
 }
 
-ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize,
-                                   bool aUseEmbeddedBitmap, bool aForceGDIMode,
+ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace,
+                                   const RefPtr<UnscaledFont>& aUnscaledFont,
+                                   Float aSize,
+                                   bool aUseEmbeddedBitmap,
+                                   bool aForceGDIMode,
                                    const gfxFontStyle* aStyle)
-    : ScaledFontBase(aSize)
+    : ScaledFontBase(aUnscaledFont, aSize)
     , mFontFace(aFontFace)
     , mUseEmbeddedBitmap(aUseEmbeddedBitmap)
     , mForceGDIMode(aForceGDIMode)
 {
   mStyle = SkFontStyle(aStyle->weight,
                        DWriteFontStretchFromStretch(aStyle->stretch),
                        aStyle->style == NS_FONT_STYLE_NORMAL ?
                        SkFontStyle::kUpright_Slant : SkFontStyle::kItalic_Slant);
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -14,25 +14,31 @@ struct gfxFontStyle;
 
 namespace mozilla {
 namespace gfx {
 
 class ScaledFontDWrite final : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
-  ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
-    : ScaledFontBase(aSize)
+  ScaledFontDWrite(IDWriteFontFace *aFont,
+                   const RefPtr<UnscaledFont>& aUnscaledFont,
+                   Float aSize)
+    : ScaledFontBase(aUnscaledFont, aSize)
     , mFontFace(aFont)
     , mUseEmbeddedBitmap(false)
     , mForceGDIMode(false)
   {}
 
-  ScaledFontDWrite(IDWriteFontFace *aFontFace, Float aSize, bool aUseEmbeddedBitmap,
-                   bool aForceGDIMode, const gfxFontStyle* aStyle);
+  ScaledFontDWrite(IDWriteFontFace *aFontFace,
+                   const RefPtr<UnscaledFont>& aUnscaledFont,
+                   Float aSize,
+                   bool aUseEmbeddedBitmap,
+                   bool aForceGDIMode,
+                   const gfxFontStyle* aStyle);
 
   virtual FontType GetType() const { return FontType::DWRITE; }
 
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
--- a/gfx/2d/ScaledFontFontconfig.cpp
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -18,18 +18,19 @@ namespace mozilla {
 namespace gfx {
 
 // On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
 // an SkFontHost implementation that allows Skia to render using this.
 // This is mainly because FT_Face is not good for sharing between libraries, which
 // is a requirement when we consider runtime switchable backends and so on
 ScaledFontFontconfig::ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont,
                                            FcPattern* aPattern,
+                                           const RefPtr<UnscaledFont>& aUnscaledFont,
                                            Float aSize)
-  : ScaledFontBase(aSize),
+  : ScaledFontBase(aUnscaledFont, aSize),
     mPattern(aPattern)
 {
   SetCairoScaledFont(aScaledFont);
   FcPatternReference(aPattern);
 }
 
 ScaledFontFontconfig::~ScaledFontFontconfig()
 {
@@ -326,17 +327,17 @@ ScaledFontFontconfig::CreateFromInstance
 
   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, aSize);
+    new ScaledFontFontconfig(cairoScaledFont, pattern, nullptr, aSize);
 
   FcPatternDestroy(pattern);
 
   return scaledFont.forget();
 }
 
 already_AddRefed<ScaledFont>
 ScaledFontFontconfig::CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength, Float aSize)
--- a/gfx/2d/ScaledFontFontconfig.h
+++ b/gfx/2d/ScaledFontFontconfig.h
@@ -14,25 +14,28 @@ namespace mozilla {
 namespace gfx {
 
 class NativeFontResourceFontconfig;
 
 class ScaledFontFontconfig : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontFontconfig, override)
-  ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
+  ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern,
+                       const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
   ~ScaledFontFontconfig();
 
   FontType GetType() const override { return FontType::FONTCONFIG; }
 
 #ifdef USE_SKIA
   SkTypeface* GetSkTypeface() override;
 #endif
 
+  bool CanSerialize() override { return true; }
+
   bool GetFontFileData(FontFileDataOutput aDataCallback, void* aBaton) override;
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   bool GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) override;
 
   static already_AddRefed<ScaledFont>
     CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength, Float aSize);
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -62,18 +62,20 @@ CreateCTFontFromCGFontWithVariations(CGF
         ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc);
         CFRelease(varDesc);
     } else {
         ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, nullptr);
     }
     return ctFont;
 }
 
-ScaledFontMac::ScaledFontMac(CGFontRef aFont, Float aSize)
-  : ScaledFontBase(aSize)
+ScaledFontMac::ScaledFontMac(CGFontRef aFont,
+                             const RefPtr<UnscaledFont>& aUnscaledFont,
+                             Float aSize)
+  : ScaledFontBase(aUnscaledFont, aSize)
 {
   if (!sSymbolLookupDone) {
     CTFontDrawGlyphsPtr =
       (CTFontDrawGlyphsFuncT*)dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
     sSymbolLookupDone = true;
   }
 
   // XXX: should we be taking a reference
--- a/gfx/2d/ScaledFontMac.h
+++ b/gfx/2d/ScaledFontMac.h
@@ -36,17 +36,17 @@ public:
 private:
   Color mFontSmoothingBackgroundColor;
 };
 
 class ScaledFontMac : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontMac)
-  ScaledFontMac(CGFontRef aFont, Float aSize);
+  ScaledFontMac(CGFontRef aFont, const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize);
   virtual ~ScaledFontMac();
 
   virtual FontType GetType() const { return FontType::MAC; }
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
 #endif
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
--- a/gfx/2d/ScaledFontWin.cpp
+++ b/gfx/2d/ScaledFontWin.cpp
@@ -17,18 +17,20 @@
 #include "cairo-win32.h"
 #endif
 
 #include "HelpersWinFonts.h"
 
 namespace mozilla {
 namespace gfx {
 
-ScaledFontWin::ScaledFontWin(const LOGFONT* aFont, Float aSize)
-  : ScaledFontBase(aSize)
+ScaledFontWin::ScaledFontWin(const LOGFONT* aFont,
+                             const RefPtr<UnscaledFont>& aUnscaledFont,
+                             Float aSize)
+  : ScaledFontBase(aUnscaledFont, aSize)
   , mLogFont(*aFont)
 {
 }
 
 bool
 ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
 {
   AutoDC dc;
@@ -75,17 +77,17 @@ ScaledFontWin::GetFontDescriptor(FontDes
 already_AddRefed<ScaledFont>
 ScaledFontWin::CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength, Float aSize)
 {
   NativeFont nativeFont;
   nativeFont.mType = NativeFontType::GDI_FONT_FACE;
   nativeFont.mFont = (void*)aData;
 
   RefPtr<ScaledFont> font =
-    Factory::CreateScaledFontForNativeFont(nativeFont, aSize);
+    Factory::CreateScaledFontForNativeFont(nativeFont, nullptr, aSize);
 
 #ifdef USE_CAIRO_SCALED_FONT
   static_cast<ScaledFontBase*>(font.get())->PopulateCairoScaledFont();
 #endif
 
   return font.forget();
 }
 
--- a/gfx/2d/ScaledFontWin.h
+++ b/gfx/2d/ScaledFontWin.h
@@ -11,17 +11,19 @@
 
 namespace mozilla {
 namespace gfx {
 
 class ScaledFontWin : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
-  ScaledFontWin(const LOGFONT* aFont, Float aSize);
+  ScaledFontWin(const LOGFONT* aFont,
+                const RefPtr<UnscaledFont>& aUnscaledFont,
+                Float aSize);
 
   virtual FontType GetType() const { return FontType::GDI; }
 
   bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) override;
 
   bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
 
   virtual bool GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) override;
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -145,17 +145,18 @@ enum class BackendType : int8_t {
 
 enum class FontType : int8_t {
   DWRITE,
   GDI,
   MAC,
   SKIA,
   CAIRO,
   COREGRAPHICS,
-  FONTCONFIG
+  FONTCONFIG,
+  FREETYPE
 };
 
 enum class NativeSurfaceType : int8_t {
   D3D10_TEXTURE,
   CAIRO_CONTEXT,
   CGCONTEXT,
   CGCONTEXT_ACCELERATED,
   OPENGL_TEXTURE
new file mode 100644
--- /dev/null
+++ b/gfx/2d/UnscaledFontDWrite.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_GFX_UNSCALEDFONTDWRITE_H_
+#define MOZILLA_GFX_UNSCALEDFONTDWRITE_H_
+
+#include <dwrite.h>
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class UnscaledFontDWrite final : public UnscaledFont
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontDWrite, override)
+  explicit UnscaledFontDWrite(const RefPtr<IDWriteFontFace>& aFontFace,
+                              DWRITE_FONT_SIMULATIONS aSimulations =
+                                DWRITE_FONT_SIMULATIONS_NONE)
+    : mFontFace(aFontFace),
+      mSimulations(aSimulations)
+  {}
+
+  FontType GetType() const override { return FontType::DWRITE; }
+
+  const RefPtr<IDWriteFontFace> GetFontFace() const { return mFontFace; }
+  DWRITE_FONT_SIMULATIONS GetSimulations() const { return mSimulations; }
+
+private:
+  RefPtr<IDWriteFontFace> mFontFace;
+  DWRITE_FONT_SIMULATIONS mSimulations;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_UNSCALEDFONTDWRITE_H_ */
+
new file mode 100644
--- /dev/null
+++ b/gfx/2d/UnscaledFontFreeType.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_GFX_UNSCALEDFONTFREETYPE_H_
+#define MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_
+
+#include <cairo-ft.h>
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class UnscaledFontFreeType : public UnscaledFont
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontFreeType, override)
+  explicit UnscaledFontFreeType(FT_Face aFace)
+    : mFace(aFace)
+    , mIndex(0)
+  {}
+  explicit UnscaledFontFreeType(const char* aFile,
+                                uint32_t aIndex = 0)
+    : mFace(nullptr)
+    , mFile(aFile)
+    , mIndex(aIndex)
+  {}
+
+  FontType GetType() const override { return FontType::FREETYPE; }
+
+  FT_Face GetFace() const { return mFace; }
+  const char* GetFile() const { return mFile.c_str(); }
+  uint32_t GetIndex() const { return mIndex; }
+
+private:
+  FT_Face mFace;
+  std::string mFile;
+  uint32_t mIndex;
+};
+
+#ifdef MOZ_WIDGET_GTK
+class UnscaledFontFontconfig : public UnscaledFontFreeType
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontFontconfig, override)
+  explicit UnscaledFontFontconfig(FT_Face aFace)
+    : UnscaledFontFreeType(aFace)
+  {}
+  explicit UnscaledFontFontconfig(const char* aFile,
+                                  uint32_t aIndex = 0)
+    : UnscaledFontFreeType(aFile, aIndex)
+  {}
+
+  FontType GetType() const override { return FontType::FONTCONFIG; }
+};
+#endif
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_UNSCALEDFONTFREETYPE_H_ */
+
new file mode 100644
--- /dev/null
+++ b/gfx/2d/UnscaledFontGDI.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_GFX_UNSCALEDFONTGDI_H_
+#define MOZILLA_GFX_UNSCALEDFONTGDI_H_
+
+#include "2D.h"
+#include <windows.h>
+
+namespace mozilla {
+namespace gfx {
+
+class UnscaledFontGDI final : public UnscaledFont
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontGDI, override)
+  explicit UnscaledFontGDI(const LOGFONT& aLogFont)
+    : mLogFont(aLogFont)
+  {}
+
+  FontType GetType() const override { return FontType::GDI; }
+
+  const LOGFONT& GetLogFont() const { return mLogFont; }
+
+private:
+  LOGFONT mLogFont;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_UNSCALEDFONTGDI_H_ */
+
new file mode 100644
--- /dev/null
+++ b/gfx/2d/UnscaledFontMac.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_GFX_UNSCALEDFONTMAC_H_
+#define MOZILLA_GFX_UNSCALEDFONTMAC_H_
+
+#ifdef MOZ_WIDGET_COCOA
+#include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreText/CoreText.h>
+#endif
+
+#include "2D.h"
+
+namespace mozilla {
+namespace gfx {
+
+class UnscaledFontMac final : public UnscaledFont
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFontMac, override)
+  explicit UnscaledFontMac(CGFontRef aFont)
+    : mFont(aFont)
+  {
+    CFRetain(mFont);
+  }
+  ~UnscaledFontMac()
+  {
+    CFRelease(mFont);
+  }
+
+  FontType GetType() const override { return FontType::MAC; }
+
+  CGFontRef GetFont() const { return mFont; }
+
+private:
+  CGFontRef mFont;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_UNSCALEDFONTMAC_H_ */
+
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -60,22 +60,27 @@ EXPORTS.mozilla.gfx += [
     'UserData.h',
 ]
 
 EXPORTS.mozilla.gfx += ['ssse3-scaler.h']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
     EXPORTS.mozilla.gfx += [
         'MacIOSurface.h',
+        'UnscaledFontMac.h',
     ]
     UNIFIED_SOURCES += [
         'NativeFontResourceMac.cpp',
         'ScaledFontMac.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    EXPORTS.mozilla.gfx += [
+        'UnscaledFontDWrite.h',
+        'UnscaledFontGDI.h',
+    ]
     SOURCES += [
         'DrawTargetD2D1.cpp',
         'ExtendInputEffectD2D1.cpp',
         'FilterNodeD2D1.cpp',
         'JobScheduler_win32.cpp',
         'NativeFontResourceDWrite.cpp',
         'NativeFontResourceGDI.cpp',
         'PathD2D.cpp',
@@ -86,16 +91,21 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
     ]
     DEFINES['WIN32'] = True
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
     SOURCES += [
         'JobScheduler_posix.cpp',
     ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3'):
+    EXPORTS.mozilla.gfx += [
+        'UnscaledFontFreeType.h',
+    ]
+
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
     SOURCES += [
         'NativeFontResourceFontconfig.cpp',
         'ScaledFontFontconfig.cpp',
     ]
 
 if CONFIG['MOZ_ENABLE_SKIA']:
     UNIFIED_SOURCES += [
--- a/gfx/thebes/gfxAndroidPlatform.h
+++ b/gfx/thebes/gfxAndroidPlatform.h
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * 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 GFX_PLATFORM_ANDROID_H
 #define GFX_PLATFORM_ANDROID_H
 
-#include "gfxFT2Fonts.h"
 #include "gfxPlatform.h"
 #include "gfxUserFontSet.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 
 namespace mozilla {
     namespace dom {
         class FontListEntry;
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -22,16 +22,17 @@
 
 #include "gfxGDIFontList.h"
 
 #include "nsIWindowsRegKey.h"
 
 #include "harfbuzz/hb.h"
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 using mozilla::intl::OSPreferences;
 
 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
                                LogLevel::Debug, args)
 #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
                                    LogLevel::Debug)
 
@@ -574,17 +575,36 @@ gfxDWriteFontEntry::ReadCMAP(FontInfoDat
 
     return rv;
 }
 
 gfxFont *
 gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
                                        bool aNeedsBold)
 {
-    return new gfxDWriteFont(this, aFontStyle, aNeedsBold);
+    WeakPtr<UnscaledFont>& unscaledFontPtr =
+        aNeedsBold ? mUnscaledFontBold : mUnscaledFont;
+    RefPtr<UnscaledFontDWrite> unscaledFont =
+        static_cast<UnscaledFontDWrite*>(unscaledFontPtr.get());
+    if (!unscaledFont) {
+        DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
+        if (aNeedsBold) {
+            sims |= DWRITE_FONT_SIMULATIONS_BOLD;
+        }
+        RefPtr<IDWriteFontFace> fontFace;
+        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), sims);
+        if (NS_FAILED(rv)) {
+            return nullptr;
+        }
+
+        unscaledFont = new UnscaledFontDWrite(fontFace, sims);
+        unscaledFontPtr = unscaledFont;
+    }
+
+    return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
 }
 
 nsresult
 gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
                                    DWRITE_FONT_SIMULATIONS aSimulations)
 {
     // initialize mFontFace if this hasn't been done before
     if (!mFontFace) {
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -12,16 +12,18 @@
 #include "gfxFont.h"
 #include "gfxUserFontSet.h"
 #include "cairo-win32.h"
 
 #include "gfxPlatformFontList.h"
 #include "gfxPlatform.h"
 #include <algorithm>
 
+#include "mozilla/gfx/UnscaledFontDWrite.h"
+
 
 /**
  * gfxDWriteFontFamily is a class that describes one of the fonts on the
  * users system.  It holds each gfxDWriteFontEntry (maps more directly to
  * a font face) which holds font type, charset info and character map info.
  */
 class gfxDWriteFontEntry;
 
@@ -196,16 +198,19 @@ protected:
     // font face corresponding to the mFont/mFontFile *without* any DWrite
     // style simulations applied
     RefPtr<IDWriteFontFace> mFontFace;
 
     DWRITE_FONT_FACE_TYPE mFaceType;
 
     int8_t mIsCJK;
     bool mForceGDIClassic;
+
+    mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
+    mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFontBold;
 };
 
 // custom text renderer used to determine the fallback font for a given char
 class DWriteFontFallbackRenderer final : public IDWriteTextRenderer
 {
 public:
     explicit DWriteFontFallbackRenderer(IDWriteFactory *aFactory)
         : mRefCount(0)
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -66,50 +66,39 @@ UsingClearType()
     {
         return true;
     }
     return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // gfxDWriteFont
-gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
+gfxDWriteFont::gfxDWriteFont(const RefPtr<UnscaledFontDWrite>& aUnscaledFont,
+                             gfxFontEntry *aFontEntry,
                              const gfxFontStyle *aFontStyle,
                              bool aNeedsBold,
                              AntialiasOption anAAOption)
-    : gfxFont(aFontEntry, aFontStyle, anAAOption)
+    : gfxFont(aUnscaledFont, aFontEntry, aFontStyle, anAAOption)
     , mCairoFontFace(nullptr)
     , mMetrics(nullptr)
     , mSpaceGlyph(0)
     , mNeedsOblique(false)
     , mNeedsBold(aNeedsBold)
     , mUseSubpixelPositions(false)
     , mAllowManualShowGlyphs(true)
 {
-    gfxDWriteFontEntry *fe =
-	        static_cast<gfxDWriteFontEntry*>(aFontEntry);
-    nsresult rv;
-    DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
     if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
-        fe->IsUpright() &&
+        aFontEntry->IsUpright() &&
         GetStyle()->allowSyntheticStyle) {
             // For this we always use the font_matrix for uniformity. Not the
             // DWrite simulation.
             mNeedsOblique = true;
     }
-    if (aNeedsBold) {
-        sims |= DWRITE_FONT_SIMULATIONS_BOLD;
-    }
 
-    rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
-
-    if (NS_FAILED(rv)) {
-        mIsValid = false;
-        return;
-    }
+    mFontFace = aUnscaledFont->GetFontFace();
 
     ComputeMetrics(anAAOption);
 }
 
 gfxDWriteFont::~gfxDWriteFont()
 {
     if (mCairoFontFace) {
         cairo_font_face_destroy(mCairoFontFace);
@@ -126,17 +115,18 @@ gfxDWriteFont::UpdateClearTypeUsage()
   Factory::UpdateSystemTextQuality();
   mUseClearType = UsingClearType();
 }
 
 UniquePtr<gfxFont>
 gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
 {
     auto entry = static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
-    return MakeUnique<gfxDWriteFont>(entry, &mStyle, mNeedsBold, anAAOption);
+    RefPtr<UnscaledFontDWrite> unscaledFont = static_cast<UnscaledFontDWrite*>(mUnscaledFont.get());
+    return MakeUnique<gfxDWriteFont>(unscaledFont, entry, &mStyle, mNeedsBold, anAAOption);
 }
 
 const gfxFont::Metrics&
 gfxDWriteFont::GetHorizontalMetrics()
 {
     return *mMetrics;
 }
 
@@ -689,31 +679,34 @@ gfxDWriteFont::GetScaledFont(mozilla::gf
   }
 
   NativeFont nativeFont;
   nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
   nativeFont.mFont = GetFontFace();
 
   if (wantCairo) {
     mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
+                                                        GetUnscaledFont(),
                                                         GetAdjustedSize(),
                                                         GetCairoScaledFont());
   } else if (aTarget->GetBackendType() == BackendType::SKIA) {
     gfxDWriteFontEntry *fe =
         static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
     bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
 
     const gfxFontStyle* fontStyle = GetStyle();
     mAzureScaledFont =
             Factory::CreateScaledFontForDWriteFont(mFontFace, fontStyle,
+                                                   GetUnscaledFont(),
                                                    GetAdjustedSize(),
                                                    useEmbeddedBitmap,
                                                    GetForceGDIClassic());
   } else {
     mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
+                                                            GetUnscaledFont(),
                                                             GetAdjustedSize());
   }
 
   mAzureScaledFontIsCairo = wantCairo;
 
   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
   return scaledFont.forget();
 }
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -12,23 +12,26 @@
 
 #include "gfxFont.h"
 #include "gfxUserFontSet.h"
 #include "cairo-win32.h"
 
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 
+#include "mozilla/gfx/UnscaledFontDWrite.h"
+
 /**
  * \brief Class representing a font face for a font entry.
  */
 class gfxDWriteFont : public gfxFont 
 {
 public:
-    gfxDWriteFont(gfxFontEntry *aFontEntry,
+    gfxDWriteFont(const RefPtr<mozilla::gfx::UnscaledFontDWrite>& aUnscaledFont,
+                  gfxFontEntry *aFontEntry,
                   const gfxFontStyle *aFontStyle,
                   bool aNeedsBold = false,
                   AntialiasOption = kAntialiasDefault);
     ~gfxDWriteFont();
 
     static void UpdateClearTypeUsage();
 
     mozilla::UniquePtr<gfxFont>
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -7,20 +7,21 @@
 #include "gfxFT2Utils.h"
 #include "harfbuzz/hb.h"
 #include "mozilla/Likely.h"
 #include "gfxFontConstants.h"
 #include "gfxFontUtils.h"
 
 using namespace mozilla::gfx;
 
-gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
+gfxFT2FontBase::gfxFT2FontBase(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
+                               cairo_scaled_font_t *aScaledFont,
                                gfxFontEntry *aFontEntry,
                                const gfxFontStyle *aFontStyle)
-    : gfxFont(aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
+    : gfxFont(aUnscaledFont, aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont),
       mSpaceGlyph(0),
       mHasMetrics(false)
 {
     cairo_scaled_font_reference(mScaledFont);
     gfxFT2LockedFace face(this);
     mFUnitsConvFactor = face.XScale();
 }
 
--- a/gfx/thebes/gfxFT2FontBase.h
+++ b/gfx/thebes/gfxFT2FontBase.h
@@ -5,20 +5,22 @@
 
 #ifndef GFX_FT2FONTBASE_H
 #define GFX_FT2FONTBASE_H
 
 #include "cairo.h"
 #include "gfxContext.h"
 #include "gfxFont.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/UnscaledFontFreeType.h"
 
 class gfxFT2FontBase : public gfxFont {
 public:
-    gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
+    gfxFT2FontBase(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
+                   cairo_scaled_font_t *aScaledFont,
                    gfxFontEntry *aFontEntry,
                    const gfxFontStyle *aFontStyle);
     virtual ~gfxFT2FontBase();
 
     uint32_t GetGlyph(uint32_t aCharCode);
     void GetGlyphExtents(uint32_t aGlyph,
                          cairo_text_extents_t* aExtents);
     virtual uint32_t GetSpaceGlyph() override;
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -45,16 +45,17 @@
 
 #include "mozilla/Preferences.h"
 #include "mozilla/scache/StartupCache.h"
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 
 static LazyLogModule sFontInfoLog("fontInfoLog");
 
 #undef LOG
 #define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug)
 
 static cairo_user_data_key_t sFTUserFontDataKey;
@@ -232,17 +233,30 @@ FT2FontEntry::~FT2FontEntry()
 
 gfxFont*
 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
     cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
     if (!scaledFont) {
         return nullptr;
     }
-    gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
+
+    RefPtr<UnscaledFontFreeType> unscaledFont =
+        static_cast<UnscaledFontFreeType*>(mUnscaledFont.get());
+    if (!unscaledFont) {
+        unscaledFont =
+            mFilename.IsEmpty() ?
+                new UnscaledFontFreeType(mFTFace) :
+                new UnscaledFontFreeType(mFilename.BeginReading(),
+                                         mFTFontIndex);
+        mUnscaledFont = unscaledFont;
+    }
+
+    gfxFont *font = new gfxFT2Font(unscaledFont, scaledFont, this,
+                                   aFontStyle, aNeedsBold);
     cairo_scaled_font_destroy(scaledFont);
     return font;
 }
 
 /* static */
 FT2FontEntry*
 FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
                               uint16_t aWeight,
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -3,16 +3,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 GFX_FT2FONTLIST_H
 #define GFX_FT2FONTLIST_H
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxPlatformFontList.h"
+#include "mozilla/gfx/UnscaledFontFreeType.h"
 
 namespace mozilla {
     namespace dom {
         class FontListEntry;
     };
 };
 using mozilla::dom::FontListEntry;
 
@@ -89,16 +90,18 @@ public:
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const override;
 
     FT_Face mFTFace;
     cairo_font_face_t *mFontFace;
 
     nsCString mFilename;
     uint8_t   mFTFontIndex;
+
+    mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
 };
 
 class FT2FontFamily : public gfxFontFamily
 {
 public:
     // Flags to indicate whether a font should be "visible" in the global
     // font list (available for use in font-family), or "hidden" (available
     // only to support a matching data: URI used in @font-face).
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -151,21 +151,22 @@ gfxFT2Font::AddRange(const char16_t *aTe
             details.mYOffset = 0;
             gfxShapedText::CompressedGlyph g;
             g.SetComplex(charGlyphs[aOffset].IsClusterStart(), true, 1);
             aShapedText->SetGlyphs(aOffset, g, &details);
         }
     }
 }
 
-gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
+gfxFT2Font::gfxFT2Font(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
+                       cairo_scaled_font_t *aCairoFont,
                        FT2FontEntry *aFontEntry,
                        const gfxFontStyle *aFontStyle,
                        bool aNeedsBold)
-    : gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle)
+    : gfxFT2FontBase(aUnscaledFont, aCairoFont, aFontEntry, aFontStyle)
     , mCharGlyphCache(32)
 {
     NS_ASSERTION(mFontEntry, "Unable to find font entry for font.  Something is whack.");
     mApplySyntheticBold = aNeedsBold;
 }
 
 gfxFT2Font::~gfxFT2Font()
 {
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -14,17 +14,18 @@
 #include "gfxContext.h"
 #include "gfxFontUtils.h"
 #include "gfxUserFontSet.h"
 
 class FT2FontEntry;
 
 class gfxFT2Font : public gfxFT2FontBase {
 public: // new functions
-    gfxFT2Font(cairo_scaled_font_t *aCairoFont,
+    gfxFT2Font(const RefPtr<mozilla::gfx::UnscaledFontFreeType>& aUnscaledFont,
+               cairo_scaled_font_t *aCairoFont,
                FT2FontEntry *aFontEntry,
                const gfxFontStyle *aFontStyle,
                bool aNeedsBold);
     virtual ~gfxFT2Font ();
 
     FT2FontEntry *GetFontEntry();
 
     struct CachedGlyphData {
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -34,16 +34,17 @@
 #include "gfxPlatformGtk.h"
 #endif
 
 #ifdef MOZ_X11
 #include "mozilla/X11Util.h"
 #endif
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 
 #ifndef FC_POSTSCRIPT_NAME
 #define FC_POSTSCRIPT_NAME  "postscriptname"      /* String */
 #endif
 
 #define PRINTING_FC_PROPERTY "gfx.printing"
 
@@ -791,16 +792,43 @@ PreparePattern(FcPattern* aPattern, bool
         }
 #endif // MOZ_X11
 #endif // MOZ_WIDGET_GTK
     }
 
     FcDefaultSubstitute(aPattern);
 }
 
+void
+gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
+    if (aIndex > 0) {
+        WeakPtr<UnscaledFont> front =
+            Move(mUnscaledFonts[aIndex]);
+        for (size_t i = aIndex; i > 0; i--) {
+            mUnscaledFonts[i] = Move(mUnscaledFonts[i-1]);
+        }
+        mUnscaledFonts[0] = Move(front);
+    }
+}
+
+already_AddRefed<UnscaledFontFontconfig>
+gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const char* aFile, uint32_t aIndex) {
+    for (size_t i = 0; i < kNumEntries; i++) {
+        UnscaledFontFontconfig* entry =
+            static_cast<UnscaledFontFontconfig*>(mUnscaledFonts[i].get());
+        if (entry &&
+            !strcmp(entry->GetFile(), aFile) &&
+            entry->GetIndex() == aIndex) {
+            MoveToFront(i);
+            return do_AddRef(entry);
+        }
+    }
+    return nullptr;
+}
+
 static inline gfxFloat
 SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
 {
     return aStyle.sizeAdjust >= 0.0 ?
                 aStyle.GetAdjustedSize(aEntry->GetAspect()) :
                 aStyle.size;
 }
 
@@ -843,18 +871,41 @@ gfxFontconfigFontEntry::CreateFontInstan
         (FcFontRenderPrepare(nullptr, pattern, mFontPattern));
     if (!renderPattern) {
         NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
         return nullptr;
     }
 
     cairo_scaled_font_t* scaledFont =
         CreateScaledFont(renderPattern, size, aFontStyle, aNeedsBold);
+
+    const FcChar8* file = ToFcChar8Ptr("");
+    int index = 0;
+    if (!mFontData) {
+        if (FcPatternGetString(renderPattern, FC_FILE, 0,
+                               const_cast<FcChar8**>(&file)) != FcResultMatch ||
+            FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) != FcResultMatch) {
+            NS_WARNING("No file in Fontconfig pattern for font instance");
+            return nullptr;
+        }
+    }
+
+    RefPtr<UnscaledFontFontconfig> unscaledFont =
+        mUnscaledFontCache.Lookup(ToCharPtr(file), index);
+    if (!unscaledFont) {
+        unscaledFont =
+            mFontData ?
+                new UnscaledFontFontconfig(mFTFace) :
+                new UnscaledFontFontconfig(ToCharPtr(file), index);
+        mUnscaledFontCache.Add(unscaledFont);
+    }
+
     gfxFont* newFont =
-        new gfxFontconfigFont(scaledFont, renderPattern, size,
+        new gfxFontconfigFont(unscaledFont, scaledFont,
+                              renderPattern, size,
                               this, aFontStyle, aNeedsBold);
     cairo_scaled_font_destroy(scaledFont);
 
     return newFont;
 }
 
 nsresult
 gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
@@ -1062,23 +1113,24 @@ gfxFontconfigFontFamily::FindAllFontsFor
         skipped++;
     }
     // Remove any compacted entries from the current group.
     if (skipped) {
         aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
     }
 }
 
-gfxFontconfigFont::gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
+gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
+                                     cairo_scaled_font_t *aScaledFont,
                                      FcPattern *aPattern,
                                      gfxFloat aAdjustedSize,
                                      gfxFontEntry *aFontEntry,
                                      const gfxFontStyle *aFontStyle,
                                      bool aNeedsBold) :
-    gfxFontconfigFontBase(aScaledFont, aPattern, aFontEntry, aFontStyle)
+    gfxFontconfigFontBase(aUnscaledFont, aScaledFont, aPattern, aFontEntry, aFontStyle)
 {
     mAdjustedSize = aAdjustedSize;
 }
 
 gfxFontconfigFont::~gfxFontconfigFont()
 {
 }
 
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -163,16 +163,36 @@ protected:
     // include color glyphs that fontconfig would overlook, and for fonts
     // loaded via @font-face.
     bool      mIgnoreFcCharmap;
 
     double    mAspect;
 
     // data font
     const uint8_t* mFontData;
+
+    class UnscaledFontCache
+    {
+    public:
+        already_AddRefed<mozilla::gfx::UnscaledFontFontconfig>
+        Lookup(const char* aFile, uint32_t aIndex);
+
+        void Add(const RefPtr<mozilla::gfx::UnscaledFontFontconfig>& aUnscaledFont) {
+            mUnscaledFonts[kNumEntries-1] = aUnscaledFont;
+            MoveToFront(kNumEntries-1);
+        }
+
+    private:
+        void MoveToFront(size_t aIndex);
+
+        static const size_t kNumEntries = 3;
+        mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFonts[kNumEntries];
+    };
+
+    UnscaledFontCache mUnscaledFontCache;
 };
 
 class gfxFontconfigFontFamily : public gfxFontFamily {
 public:
     explicit gfxFontconfigFontFamily(const nsAString& aName) :
         gfxFontFamily(aName),
         mContainsAppFonts(false),
         mHasNonScalableFaces(false)
@@ -200,17 +220,18 @@ protected:
     nsTArray<nsCountedRef<FcPattern> > mFontPatterns;
 
     bool      mContainsAppFonts;
     bool      mHasNonScalableFaces;
 };
 
 class gfxFontconfigFont : public gfxFontconfigFontBase {
 public:
-    gfxFontconfigFont(cairo_scaled_font_t *aScaledFont,
+    gfxFontconfigFont(const RefPtr<mozilla::gfx::UnscaledFontFontconfig> &aUnscaledFont,
+                      cairo_scaled_font_t *aScaledFont,
                       FcPattern *aPattern,
                       gfxFloat aAdjustedSize,
                       gfxFontEntry *aFontEntry,
                       const gfxFontStyle *aFontStyle,
                       bool aNeedsBold);
 
 protected:
     virtual ~gfxFontconfigFont();
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -830,26 +830,28 @@ gfxFont::RunMetrics::CombineWith(const R
             (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
     } else {
         mBoundingBox =
             mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
     }
     mAdvanceWidth += aOther.mAdvanceWidth;
 }
 
-gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
+                 gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
                  AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
     mScaledFont(aScaledFont),
     mFontEntry(aFontEntry), mIsValid(true),
     mApplySyntheticBold(false),
     mMathInitialized(false),
     mStyle(*aFontStyle),
     mAdjustedSize(0.0),
     mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
-    mAntialiasOption(anAAOption)
+    mAntialiasOption(anAAOption),
+    mUnscaledFont(aUnscaledFont)
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     ++gFontCount;
 #endif
     mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
 }
 
 gfxFont::~gfxFont()
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1380,17 +1380,18 @@ protected:
             // possibly recycling within some time limit
             cache->NotifyReleased(this);
         } else {
             // The cache may have already been shut down.
             delete this;
         }
     }
 
-    gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+    gfxFont(const RefPtr<mozilla::gfx::UnscaledFont>& aUnscaledFont,
+            gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
             AntialiasOption anAAOption = kAntialiasDefault,
             cairo_scaled_font_t *aScaledFont = nullptr);
 
 public:
     virtual ~gfxFont();
 
     bool Valid() const {
         return mIsValid;
@@ -1796,18 +1797,24 @@ public:
         FONT_TYPE_MAC,
         FONT_TYPE_OS2,
         FONT_TYPE_CAIRO,
         FONT_TYPE_FONTCONFIG
     } FontType;
 
     virtual FontType GetType() const = 0;
 
+    const RefPtr<mozilla::gfx::UnscaledFont>& GetUnscaledFont() const {
+        return mUnscaledFont;
+    }
+
     virtual already_AddRefed<mozilla::gfx::ScaledFont> GetScaledFont(DrawTarget* aTarget)
-    { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
+    {
+        return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this);
+    }
 
     bool KerningDisabled() {
         return mKerningSet && !mKerningEnabled;
     }
 
     /**
      * Subclass this object to be notified of glyph changes. Delete the object
      * when no longer needed.
@@ -2115,16 +2122,17 @@ protected:
     // is actually enabled
     mozilla::UniquePtr<gfxFontShaper>   mHarfBuzzShaper;
     mozilla::UniquePtr<gfxFontShaper>   mGraphiteShaper;
 
     // if a userfont with unicode-range specified, contains map of *possible*
     // ranges supported by font
     RefPtr<gfxCharacterMap> mUnicodeRangeMap;
 
+    RefPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
     RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont;
 
     // For vertical metrics, created on demand.
     mozilla::UniquePtr<const Metrics> mVerticalMetrics;
 
     // Table used for MathML layout.
     mozilla::UniquePtr<gfxMathTable> mMathTable;
 
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -15,16 +15,17 @@
 #include "nsTHashtable.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsUnicodeScriptCodes.h"
 #include "nsDataHashtable.h"
 #include "harfbuzz/hb.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/WeakPtr.h"
 
 typedef struct gr_face gr_face;
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 struct gfxFontStyle;
--- a/gfx/thebes/gfxFontconfigUtils.h
+++ b/gfx/thebes/gfxFontconfigUtils.h
@@ -33,21 +33,22 @@ template <>
 class nsAutoRefTraits<FcCharSet> : public nsPointerRefTraits<FcCharSet>
 {
 public:
     static void Release(FcCharSet *ptr) { FcCharSetDestroy(ptr); }
 };
 
 class gfxFontconfigFontBase : public gfxFT2FontBase {
 public:
-    gfxFontconfigFontBase(cairo_scaled_font_t *aScaledFont,
+    gfxFontconfigFontBase(const RefPtr<mozilla::gfx::UnscaledFontFontconfig>& aUnscaledFont,
+                          cairo_scaled_font_t *aScaledFont,
                           FcPattern *aPattern,
                           gfxFontEntry *aFontEntry,
                           const gfxFontStyle *aFontStyle)
-      : gfxFT2FontBase(aScaledFont, aFontEntry, aFontStyle)
+      : gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle)
       , mPattern(aPattern) { }
 
     virtual FontType GetType() const override { return FONT_TYPE_FONTCONFIG; }
     virtual FcPattern *GetPattern() const { return mPattern; }
 
 private:
     nsCountedRef<FcPattern> mPattern;
 };
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -39,25 +39,29 @@ GetCairoAntialiasOption(gfxFont::Antiali
         return CAIRO_ANTIALIAS_SUBPIXEL;
     }
 }
 
 gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
                        const gfxFontStyle *aFontStyle,
                        bool aNeedsBold,
                        AntialiasOption anAAOption)
-    : gfxFont(aFontEntry, aFontStyle, anAAOption),
+    : gfxFont(nullptr, aFontEntry, aFontStyle, anAAOption),
       mFont(nullptr),
       mFontFace(nullptr),
       mMetrics(nullptr),
       mSpaceGlyph(0),
       mNeedsBold(aNeedsBold),
       mScriptCache(nullptr)
 {
     Initialize();
+
+    if (mFont) {
+        mUnscaledFont = aFontEntry->LookupUnscaledFont(mFont);
+    }
 }
 
 gfxGDIFont::~gfxGDIFont()
 {
     if (mScaledFont) {
         cairo_scaled_font_destroy(mScaledFont);
     }
     if (mFontFace) {
@@ -219,16 +223,22 @@ gfxGDIFont::Initialize()
     // this may end up being zero
     mAdjustedSize = ROUND(mAdjustedSize);
     FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic);
     mFont = ::CreateFontIndirectW(&logFont);
 
     mMetrics = new gfxFont::Metrics;
     ::memset(mMetrics, 0, sizeof(*mMetrics));
 
+    if (!mFont) {
+        NS_WARNING("Failed creating GDI font");
+        mIsValid = false;
+        return;
+    }
+
     AutoDC dc;
     SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
     AutoSelectFont selectFont(dc.GetDC(), mFont);
 
     // Get font metrics if size > 0
     if (mAdjustedSize > 0.0) {
 
         OUTLINETEXTMETRIC oMetrics;
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -28,16 +28,17 @@
 #include "GeckoProfiler.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Telemetry.h"
 
 #include <usp10.h>
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 
 #define ROUND(x) floor((x) + 0.5)
 
 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
                                LogLevel::Debug, args)
 #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
                                    LogLevel::Debug)
@@ -241,16 +242,31 @@ GDIFontEntry::CopyFontTable(uint32_t aTa
                 return NS_OK;
             }
             return NS_ERROR_OUT_OF_MEMORY;
         }
     }
     return NS_ERROR_FAILURE;
 }
 
+already_AddRefed<UnscaledFontGDI>
+GDIFontEntry::LookupUnscaledFont(HFONT aFont)
+{
+    RefPtr<UnscaledFontGDI> unscaledFont =
+        static_cast<UnscaledFontGDI*>(mUnscaledFont.get());
+    if (!unscaledFont) {
+        LOGFONT lf;
+        GetObject(aFont, sizeof(LOGFONT), &lf);
+        unscaledFont = new UnscaledFontGDI(lf);
+        mUnscaledFont = unscaledFont;
+    }
+
+    return unscaledFont.forget();
+}
+
 void
 GDIFontEntry::FillLogFont(LOGFONTW *aLogFont,
                           uint16_t aWeight, gfxFloat aSize)
 {
     memcpy(aLogFont, &mLogFont, sizeof(LOGFONTW));
 
     aLogFont->lfHeight = (LONG)-ROUND(aSize);
 
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -5,16 +5,17 @@
 
 #ifndef GFX_GDIFONTLIST_H
 #define GFX_GDIFONTLIST_H
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxWindowsPlatform.h"
 #include "gfxPlatformFontList.h"
 #include "nsGkAtoms.h"
+#include "mozilla/gfx/UnscaledFontGDI.h"
 
 #include <windows.h>
 
 class AutoDC // get the global device context, and auto-release it on destruction
 {
 public:
     AutoDC() {
         mDC = ::GetDC(nullptr);
@@ -257,30 +258,34 @@ public:
     // to create synthetic italics (oblique) via the LOGFONT.
     // (For other types of font, this is just set to false.)
     bool mFamilyHasItalicFace : 1;
 
     gfxSparseBitSet mCharset;
     gfxSparseBitSet mUnicodeRanges;
 
 protected:
-    friend class gfxWindowsFont;
+    friend class gfxGDIFont;
 
     GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
                  uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
                  gfxUserFontData *aUserFontData, bool aFamilyHasItalicFace);
 
     void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold);
 
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    nsTArray<uint8_t>& aBuffer) override;
 
+    already_AddRefed<mozilla::gfx::UnscaledFontGDI> LookupUnscaledFont(HFONT aFont);
+
     LOGFONTW mLogFont;
+
+    mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
 };
 
 // a single font family, referencing one or more faces
 class GDIFontFamily : public gfxFontFamily
 {
 public:
     explicit GDIFontFamily(nsAString &aName) :
         gfxFontFamily(aName) {}
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -154,43 +154,45 @@ CreateVariationDictionaryOrNull(CGFontRe
         // We didn't actually set any non-default values, so throw away the
         // variations dictionary and just use the default rendering.
         return nullptr;
     }
 
     return dict.forget();
 }
 
-gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+gfxMacFont::gfxMacFont(const RefPtr<UnscaledFontMac>& aUnscaledFont,
+                       MacOSFontEntry *aFontEntry,
+                       const gfxFontStyle *aFontStyle,
                        bool aNeedsBold)
-    : gfxFont(aFontEntry, aFontStyle),
+    : gfxFont(aUnscaledFont, aFontEntry, aFontStyle),
       mCGFont(nullptr),
       mCTFont(nullptr),
       mFontFace(nullptr),
       mVariationFont(aFontEntry->HasVariations())
 {
     mApplySyntheticBold = aNeedsBold;
 
     if (mVariationFont && aFontStyle->variationSettings.Length() > 0) {
-        CGFontRef baseFont = aFontEntry->GetFontRef();
+        CGFontRef baseFont = aUnscaledFont->GetFont();
         if (!baseFont) {
             mIsValid = false;
             return;
         }
         CFDictionaryRef variations =
             CreateVariationDictionaryOrNull(baseFont, aFontStyle->variationSettings);
         if (variations) {
             mCGFont = ::CGFontCreateCopyWithVariations(baseFont, variations);
             ::CFRelease(variations);
         } else {
             ::CFRetain(baseFont);
             mCGFont = baseFont;
         }
     } else {
-        mCGFont = aFontEntry->GetFontRef();
+        mCGFont = aUnscaledFont->GetFont();
         if (!mCGFont) {
             mIsValid = false;
             return;
         }
         ::CFRetain(mCGFont);
     }
 
     // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
@@ -631,28 +633,32 @@ gfxMacFont::InitMetricsFromPlatform()
 
 already_AddRefed<ScaledFont>
 gfxMacFont::GetScaledFont(DrawTarget *aTarget)
 {
   if (!mAzureScaledFont) {
     NativeFont nativeFont;
     nativeFont.mType = NativeFontType::MAC_FONT_FACE;
     nativeFont.mFont = GetCGFontRef();
-    mAzureScaledFont = mozilla::gfx::Factory::CreateScaledFontWithCairo(nativeFont, GetAdjustedSize(), mScaledFont);
+    mAzureScaledFont =
+      Factory::CreateScaledFontWithCairo(nativeFont,
+                                         GetUnscaledFont(),
+                                         GetAdjustedSize(),
+                                         mScaledFont);
   }
 
   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
   return scaledFont.forget();
 }
 
-already_AddRefed<mozilla::gfx::GlyphRenderingOptions>
+already_AddRefed<GlyphRenderingOptions>
 gfxMacFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
 {
     if (aRunParams) {
-        return mozilla::gfx::Factory::CreateCGGlyphRenderingOptions(aRunParams->fontSmoothingBGColor);
+        return Factory::CreateCGGlyphRenderingOptions(aRunParams->fontSmoothingBGColor);
     }
     return nullptr;
 }
 
 void
 gfxMacFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                    FontCacheSizes* aSizes) const
 {
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -6,22 +6,25 @@
 #ifndef GFX_MACFONT_H
 #define GFX_MACFONT_H
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxFont.h"
 #include "cairo.h"
 #include <ApplicationServices/ApplicationServices.h>
 
+#include "mozilla/gfx/UnscaledFontMac.h"
+
 class MacOSFontEntry;
 
 class gfxMacFont : public gfxFont
 {
 public:
-    gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
+    gfxMacFont(const RefPtr<mozilla::gfx::UnscaledFontMac>& aUnscaledFont,
+               MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
                bool aNeedsBold);
 
     virtual ~gfxMacFont();
 
     CGFontRef GetCGFontRef() const { return mCGFont; }
 
     /* overrides for the pure virtual methods in gfxFont */
     uint32_t GetSpaceGlyph() override {
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -15,16 +15,18 @@
 #include "gfxPlatformFontList.h"
 #include "gfxPlatform.h"
 #include "gfxPlatformMac.h"
 
 #include "nsUnicharUtils.h"
 #include "nsTArray.h"
 #include "mozilla/LookAndFeel.h"
 
+#include "mozilla/gfx/UnscaledFontMac.h"
+
 class gfxMacPlatformFontList;
 
 // a single member of a font family (i.e. a single face, such as Times Italic)
 class MacOSFontEntry : public gfxFontEntry
 {
 public:
     friend class gfxMacPlatformFontList;
 
@@ -71,16 +73,18 @@ protected:
 
     bool mFontRefInitialized;
     bool mRequiresAAT;
     bool mIsCFF;
     bool mIsCFFInitialized;
     bool mHasVariations;
     bool mHasVariationsInitialized;
     nsTHashtable<nsUint32HashKey> mAvailableTables;
+
+    mozilla::WeakPtr<mozilla::gfx::UnscaledFont> mUnscaledFont;
 };
 
 class gfxMacPlatformFontList : public gfxPlatformFontList {
 public:
     static gfxMacPlatformFontList* PlatformFontList() {
         return static_cast<gfxMacPlatformFontList*>(sPlatformFontList);
     }
 
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -70,17 +70,17 @@
 #include "mozilla/Telemetry.h"
 #include "mozilla/gfx/2D.h"
 
 #include <unistd.h>
 #include <time.h>
 #include <dlfcn.h>
 
 using namespace mozilla;
-
+using namespace mozilla::gfx;
 using mozilla::dom::FontFamilyListEntry;
 
 // indexes into the NSArray objects that the Cocoa font manager returns
 // as the available members of a family
 #define INDEX_FONT_POSTSCRIPT_NAME 0
 #define INDEX_FONT_FACE_NAME 1
 #define INDEX_FONT_WEIGHT 2
 #define INDEX_FONT_TRAITS 3
@@ -239,17 +239,28 @@ MacOSFontEntry::ReadCMAP(FontInfoData *a
     }
 
     return rv;
 }
 
 gfxFont*
 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
-    return new gfxMacFont(this, aFontStyle, aNeedsBold);
+    RefPtr<UnscaledFontMac> unscaledFont =
+        static_cast<UnscaledFontMac*>(mUnscaledFont.get());
+    if (!unscaledFont) {
+        CGFontRef baseFont = GetFontRef();
+        if (!baseFont) {
+            return nullptr;
+        }
+        unscaledFont = new UnscaledFontMac(baseFont);
+        mUnscaledFont = unscaledFont;
+    }
+
+    return new gfxMacFont(unscaledFont, this, aFontStyle, aNeedsBold);
 }
 
 bool
 MacOSFontEntry::HasVariations()
 {
     if (!mHasVariationsInitialized) {
         mHasVariationsInitialized = true;
         mHasVariations = HasFontTable(TRUETYPE_TAG('f','v','a','r'));
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1218,16 +1218,17 @@ gfxPlatform::GetWrappedDataSourceSurface
 
 already_AddRefed<ScaledFont>
 gfxPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
 {
   NativeFont nativeFont;
   nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
   nativeFont.mFont = aFont->GetCairoScaledFont();
   return Factory::CreateScaledFontForNativeFont(nativeFont,
+                                                aFont->GetUnscaledFont(),
                                                 aFont->GetAdjustedSize());
 }
 
 void
 gfxPlatform::ComputeTileSize()
 {
   // The tile size should be picked in the parent processes
   // and sent to the child processes over IPDL GetTileSize.
@@ -2418,17 +2419,19 @@ gfxPlatform::DisableBufferRotation()
 
 already_AddRefed<ScaledFont>
 gfxPlatform::GetScaledFontForFontWithCairoSkia(DrawTarget* aTarget, gfxFont* aFont)
 {
     NativeFont nativeFont;
     if (aTarget->GetBackendType() == BackendType::CAIRO || aTarget->GetBackendType() == BackendType::SKIA) {
         nativeFont.mType = NativeFontType::CAIRO_FONT_FACE;
         nativeFont.mFont = aFont->GetCairoScaledFont();
-        return Factory::CreateScaledFontForNativeFont(nativeFont, aFont->GetAdjustedSize());
+        return Factory::CreateScaledFontForNativeFont(nativeFont,
+                                                      aFont->GetUnscaledFont(),
+                                                      aFont->GetAdjustedSize());
     }
 
     return nullptr;
 }
 
 /* static */ bool
 gfxPlatform::UsesOffMainThreadCompositing()
 {
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -598,16 +598,17 @@ gfxPlatformGtk::GetScaledFontForFont(Dra
     switch (aTarget->GetBackendType()) {
     case BackendType::CAIRO:
     case BackendType::SKIA:
         if (aFont->GetType() == gfxFont::FONT_TYPE_FONTCONFIG) {
             gfxFontconfigFontBase* fcFont = static_cast<gfxFontconfigFontBase*>(aFont);
             return Factory::CreateScaledFontForFontconfigFont(
                     fcFont->GetCairoScaledFont(),
                     fcFont->GetPattern(),
+                    fcFont->GetUnscaledFont(),
                     fcFont->GetAdjustedSize());
         }
         MOZ_FALLTHROUGH;
     default:
         return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
     }
 }
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -587,40 +587,45 @@ gfxWindowsPlatform::GetScaledFontForFont
         gfxDWriteFont *font = static_cast<gfxDWriteFont*>(aFont);
 
         NativeFont nativeFont;
         nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
         nativeFont.mFont = font->GetFontFace();
 
         if (aTarget->GetBackendType() == BackendType::CAIRO) {
           return Factory::CreateScaledFontWithCairo(nativeFont,
+                                                    font->GetUnscaledFont(),
                                                     font->GetAdjustedSize(),
                                                     font->GetCairoScaledFont());
         }
 
         return Factory::CreateScaledFontForNativeFont(nativeFont,
+                                                      font->GetUnscaledFont(),
                                                       font->GetAdjustedSize());
     }
 
     NS_ASSERTION(aFont->GetType() == gfxFont::FONT_TYPE_GDI,
         "Fonts on windows should be GDI or DWrite!");
 
     NativeFont nativeFont;
     nativeFont.mType = NativeFontType::GDI_FONT_FACE;
     LOGFONT lf;
     GetObject(static_cast<gfxGDIFont*>(aFont)->GetHFONT(), sizeof(LOGFONT), &lf);
     nativeFont.mFont = &lf;
 
     if (aTarget->GetBackendType() == BackendType::CAIRO) {
       return Factory::CreateScaledFontWithCairo(nativeFont,
+                                                aFont->GetUnscaledFont(),
                                                 aFont->GetAdjustedSize(),
                                                 aFont->GetCairoScaledFont());
     }
 
-    return Factory::CreateScaledFontForNativeFont(nativeFont, aFont->GetAdjustedSize());
+    return Factory::CreateScaledFontForNativeFont(nativeFont,
+                                                  aFont->GetUnscaledFont(),
+                                                  aFont->GetAdjustedSize());
 }
 
 static const char kFontAparajita[] = "Aparajita";
 static const char kFontArabicTypesetting[] = "Arabic Typesetting";
 static const char kFontArial[] = "Arial";
 static const char kFontArialUnicodeMS[] = "Arial Unicode MS";
 static const char kFontCambria[] = "Cambria";
 static const char kFontCambriaMath[] = "Cambria Math";
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5020,16 +5020,17 @@ nsDisplayText::nsDisplayText(nsDisplayLi
     // TODO: Ideally we'd re-use captureCtx in Paint() if we couldn't build
     // a layer here. We have to deal with the problem that the ScreenReferenceDrawTarget
     // might not be compatible with the DT used for layer rendering.
 
     GlyphArray* g = mGlyphs.AppendElement();
     std::vector<Glyph> glyphs;
     Color color;
     if (!capture->ContainsOnlyColoredGlyphs(mFont, color, glyphs)
+        || !mFont
         || !mFont->CanSerialize()
         || XRE_IsParentProcess()) {
       mFont = nullptr;
       mGlyphs.Clear();
     } else {
       g->glyphs().SetLength(glyphs.size());
       PodCopy(g->glyphs().Elements(), glyphs.data(), glyphs.size());
       g->color() = color;