Bug 1364628 - implement thread-safe variants of FT_New_Face/FT_Done_Face. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Wed, 17 May 2017 21:56:24 -0400
changeset 394171 f5c1bf433c6a2817041499a25467474f600e9f52
parent 394170 5b6d169feb92d98032768d4d7d1a611668c60954
child 394172 cb7db33245b9e38ff2e9670abd6de69ebf64e6da
push idunknown
push userunknown
push dateunknown
reviewersjrmuizel
bugs1364628
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 1364628 - implement thread-safe variants of FT_New_Face/FT_Done_Face. r=jrmuizel MozReview-Commit-ID: 6xbOm2nYhXo
gfx/2d/2D.h
gfx/2d/Factory.cpp
gfx/2d/NativeFontResourceFontconfig.cpp
gfx/2d/UnscaledFontFreeType.h
gfx/cairo/cairo/src/cairo-ft-font.c
gfx/thebes/gfxAndroidPlatform.cpp
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFcPlatformFontList.cpp
gfx/thebes/gfxFcPlatformFontList.h
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -26,48 +26,49 @@
 // outparams using the &-operator. But it will have to do as there's no easy
 // solution.
 #include "mozilla/RefPtr.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/WeakPtr.h"
 
 #include "mozilla/DebugOnly.h"
 
-#ifdef MOZ_ENABLE_FREETYPE
-#include <string>
-#endif
-
 struct _cairo_surface;
 typedef _cairo_surface cairo_surface_t;
 
 struct _cairo_scaled_font;
 typedef _cairo_scaled_font cairo_scaled_font_t;
 
 struct _FcPattern;
 typedef _FcPattern FcPattern;
 
 struct FT_LibraryRec_;
 typedef FT_LibraryRec_* FT_Library;
 
+struct FT_FaceRec_;
+typedef FT_FaceRec_* FT_Face;
+
 struct ID3D11Texture2D;
 struct ID3D11Device;
 struct ID2D1Device;
 struct IDWriteFactory;
 struct IDWriteRenderingParams;
 struct IDWriteFontFace;
 
 class GrContext;
 class SkCanvas;
 struct gfxFontStyle;
 
 struct CGContext;
 typedef struct CGContext *CGContextRef;
 
 namespace mozilla {
 
+class Mutex;
+
 namespace gfx {
 class UnscaledFont;
 }
 
 template<>
 struct WeakPtrTraits<gfx::UnscaledFont>
 {
   static void AssertSafeToAccessFromNonOwningThread()
@@ -815,17 +816,17 @@ protected:
   UserData mUserData;
   RefPtr<UnscaledFont> mUnscaledFont;
 };
 
 /**
  * Derived classes hold a native font resource from which to create
  * ScaledFonts.
  */
-class NativeFontResource : public RefCounted<NativeFontResource>
+class NativeFontResource : public external::AtomicRefCounted<NativeFontResource>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResource)
 
   /**
    * Creates a UnscaledFont using the font corresponding to the index.
    *
    * @param aIndex index for the font within the resource.
@@ -1611,18 +1612,23 @@ public:
   static already_AddRefed<GlyphRenderingOptions>
     CreateCGGlyphRenderingOptions(const Color &aFontSmoothingBackgroundColor);
 #endif
 
 #ifdef MOZ_ENABLE_FREETYPE
   static void SetFTLibrary(FT_Library aFTLibrary);
   static FT_Library GetFTLibrary();
 
+  static FT_Face NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex);
+  static FT_Face NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex);
+  static void ReleaseFTFace(FT_Face aFace);
+
 private:
   static FT_Library mFTLibrary;
+  static Mutex* mFTLock;
 public:
 #endif
 
 #ifdef WIN32
   static already_AddRefed<DrawTarget> CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat);
 
   /*
    * Attempts to create and install a D2D1 device from the supplied Direct3D11 device.
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -54,16 +54,23 @@
 #include "SourceSurfaceRawData.h"
 
 #include "DrawEventRecorder.h"
 
 #include "Logging.h"
 
 #include "mozilla/CheckedInt.h"
 
+#ifdef MOZ_ENABLE_FREETYPE
+#include "ft2build.h"
+#include FT_FREETYPE_H
+
+#include "mozilla/Mutex.h"
+#endif
+
 #if defined(MOZ_LOGGING)
 GFX2D_API mozilla::LogModule*
 GetGFX2DLog()
 {
   static mozilla::LazyLogModule sLog("gfx2d");
   return sLog;
 }
 #endif
@@ -150,24 +157,49 @@ HasCPUIDBit(unsigned int level, CPUIDReg
   if (unsigned(regs[0]) < level)
     return false;
   __cpuid((int *)regs, level);
   return !!(unsigned(regs[reg]) & bit);
 }
 #endif
 #endif
 
+#ifdef MOZ_ENABLE_FREETYPE
+extern "C" {
+
+FT_Face
+mozilla_NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex)
+{
+  return mozilla::gfx::Factory::NewFTFace(aFTLibrary, aFileName, aFaceIndex);
+}
+
+FT_Face
+mozilla_NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex)
+{
+  return mozilla::gfx::Factory::NewFTFaceFromData(aFTLibrary, aData, aDataSize, aFaceIndex);
+}
+
+void
+mozilla_ReleaseFTFace(FT_Face aFace)
+{
+  mozilla::gfx::Factory::ReleaseFTFace(aFace);
+}
+
+}
+#endif
+
 namespace mozilla {
 namespace gfx {
 
 // In Gecko, this value is managed by gfx.logging.level in gfxPrefs.
 int32_t LoggingPrefs::sGfxLogLevel = LOG_DEFAULT;
 
 #ifdef MOZ_ENABLE_FREETYPE
 FT_Library Factory::mFTLibrary = nullptr;
+Mutex* Factory::mFTLock = nullptr;
 #endif
 
 #ifdef WIN32
 static uint32_t mDeviceSeq = 0;
 ID3D11Device *Factory::mD3D11Device = nullptr;
 ID2D1Device *Factory::mD2D1Device = nullptr;
 IDWriteFactory *Factory::mDWriteFactory = nullptr;
 #endif
@@ -187,30 +219,36 @@ Factory::Init(const Config& aConfig)
   const int32_t kMinAllocPref = 10000000;
   const int32_t kMinSizePref = 2048;
   if (sConfig->mMaxAllocSize < kMinAllocPref) {
     sConfig->mMaxAllocSize = kMinAllocPref;
   }
   if (sConfig->mMaxTextureSize < kMinSizePref) {
     sConfig->mMaxTextureSize = kMinSizePref;
   }
+
+#ifdef MOZ_ENABLE_FREETYPE
+  mFTLock = new Mutex("Factory::mFTLock");
+#endif
 }
 
 void
 Factory::ShutDown()
 {
   if (sConfig) {
     delete sConfig->mLogForwarder;
     delete sConfig;
     sConfig = nullptr;
   }
 
 #ifdef MOZ_ENABLE_FREETYPE
-  if (mFTLibrary) {
-    mFTLibrary = nullptr;
+  mFTLibrary = nullptr;
+  if (mFTLock) {
+    delete mFTLock;
+    mFTLock = nullptr;
   }
 #endif
 }
 
 bool
 Factory::HasSSE2()
 {
 #if defined(__SSE2__) || defined(_M_X64) || \
@@ -617,16 +655,61 @@ Factory::SetFTLibrary(FT_Library aFTLibr
 }
 
 FT_Library
 Factory::GetFTLibrary()
 {
   MOZ_ASSERT(mFTLibrary);
   return mFTLibrary;
 }
+
+FT_Face
+Factory::NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex)
+{
+  MOZ_ASSERT(mFTLock);
+  MutexAutoLock lock(*mFTLock);
+  if (!aFTLibrary) {
+    aFTLibrary = mFTLibrary;
+  }
+  FT_Face face;
+  if (FT_New_Face(aFTLibrary, aFileName, aFaceIndex, &face) != FT_Err_Ok) {
+    return nullptr;
+  }
+  return face;
+}
+
+FT_Face
+Factory::NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex)
+{
+  MOZ_ASSERT(mFTLock);
+  MutexAutoLock lock(*mFTLock);
+  if (!aFTLibrary) {
+    aFTLibrary = mFTLibrary;
+  }
+  FT_Face face;
+  if (FT_New_Memory_Face(aFTLibrary, aData, aDataSize, aFaceIndex, &face) != FT_Err_Ok) {
+    return nullptr;
+  }
+  return face;
+}
+
+void
+Factory::ReleaseFTFace(FT_Face aFace)
+{
+  // May be called during shutdown when the lock is already destroyed.
+  // However, there are no other threads using the face by this point,
+  // so it is safe to skip locking if the lock is not around.
+  if (mFTLock) {
+    mFTLock->Lock();
+  }
+  FT_Done_Face(aFace);
+  if (mFTLock) {
+    mFTLock->Unlock();
+  }
+}
 #endif
 
 #ifdef WIN32
 already_AddRefed<DrawTarget>
 Factory::CreateDrawTargetForD3D11Texture(ID3D11Texture2D *aTexture, SurfaceFormat aFormat)
 {
   MOZ_ASSERT(aTexture);
 
--- a/gfx/2d/NativeFontResourceFontconfig.cpp
+++ b/gfx/2d/NativeFontResourceFontconfig.cpp
@@ -16,36 +16,36 @@ NativeFontResourceFontconfig::NativeFont
   : mFontData(Move(aFontData)),
     mFace(aFace)
 {
 }
 
 NativeFontResourceFontconfig::~NativeFontResourceFontconfig()
 {
   if (mFace) {
-    FT_Done_Face(mFace);
+    Factory::ReleaseFTFace(mFace);
     mFace = nullptr;
   }
 }
 
 already_AddRefed<NativeFontResourceFontconfig>
 NativeFontResourceFontconfig::Create(uint8_t *aFontData, uint32_t aDataLength)
 {
   if (!aFontData || !aDataLength) {
     return nullptr;
   }
   UniquePtr<uint8_t[]> fontData(new uint8_t[aDataLength]);
   memcpy(fontData.get(), aFontData, aDataLength);
 
-  FT_Face face;
-  if (FT_New_Memory_Face(Factory::GetFTLibrary(), fontData.get(), aDataLength, 0, &face) != FT_Err_Ok) {
+  FT_Face face = Factory::NewFTFaceFromData(nullptr, fontData.get(), aDataLength, 0);
+  if (!face) {
     return nullptr;
   }
   if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != FT_Err_Ok) {
-    FT_Done_Face(face);
+    Factory::ReleaseFTFace(face);
     return nullptr;
   }
 
   RefPtr<NativeFontResourceFontconfig> resource =
     new NativeFontResourceFontconfig(Move(fontData), face);
   return resource.forget();
 }
 
--- a/gfx/2d/UnscaledFontFreeType.h
+++ b/gfx/2d/UnscaledFontFreeType.h
@@ -28,17 +28,17 @@ public:
     : mFace(nullptr)
     , mOwnsFace(false)
     , mFile(aFile)
     , mIndex(aIndex)
   {}
   ~UnscaledFontFreeType()
   {
     if (mOwnsFace) {
-      FT_Done_Face(mFace);
+      Factory::ReleaseFTFace(mFace);
     }
   }
 
   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; }
--- a/gfx/cairo/cairo/src/cairo-ft-font.c
+++ b/gfx/cairo/cairo/src/cairo-ft-font.c
@@ -99,16 +99,20 @@ static setLcdFilterFunc setLcdFilter;
 
 /* This is the max number of FT_face objects we keep open at once
  */
 #define MAX_OPEN_FACES 10
 /* This is the maximum font size we allow to be passed to FT_Set_Char_Size
  */
 #define MAX_FONT_SIZE 1000
 
+extern FT_Face mozilla_NewFTFace(FT_Library aFTLibrary, const char* aFileName, int aFaceIndex);
+extern FT_Face mozilla_NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize, int aFaceIndex);
+extern void mozilla_ReleaseFTFace(FT_Face aFace);
+
 /**
  * SECTION:cairo-ft
  * @Title: FreeType Fonts
  * @Short_Description: Font support for FreeType
  * @See_Also: #cairo_font_face_t
  *
  * The FreeType font backend is primarily used to render text on GNU/Linux
  * systems, but can be used on other platforms too.
@@ -238,17 +242,17 @@ typedef struct _cairo_ft_unscaled_font_m
 static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL;
 
 
 static void
 _font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map,
 				  cairo_ft_unscaled_font_t *unscaled)
 {
     if (unscaled->face) {
-	FT_Done_Face (unscaled->face);
+	mozilla_ReleaseFTFace (unscaled->face);
 	unscaled->face = NULL;
 	unscaled->have_scale = FALSE;
 
 	font_map->num_open_faces--;
     }
 }
 
 static cairo_status_t
@@ -664,20 +668,18 @@ cairo_warn FT_Face
 	    if (entry == NULL)
 		break;
 
 	    _font_map_release_face_lock_held (font_map, entry);
 	}
     }
     _cairo_ft_unscaled_font_map_unlock ();
 
-    if (FT_New_Face (font_map->ft_library,
-		     unscaled->filename,
-		     unscaled->id,
-		     &face) != FT_Err_Ok)
+    face = mozilla_NewFTFace (font_map->ft_library, unscaled->filename, unscaled->id);
+    if (!face)
     {
 	unscaled->lock_count--;
 	CAIRO_MUTEX_UNLOCK (unscaled->mutex);
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return NULL;
     }
 
     unscaled->face = face;
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -90,16 +90,18 @@ gfxAndroidPlatform::gfxAndroidPlatform()
     sFreetypeMemoryRecord.free    = FreetypeReporter::Free;
     sFreetypeMemoryRecord.realloc = FreetypeReporter::Realloc;
 
     // These two calls are equivalent to FT_Init_FreeType(), but allow us to
     // provide a custom memory allocator.
     FT_New_Library(&sFreetypeMemoryRecord, &gPlatformFTLibrary);
     FT_Add_Default_Modules(gPlatformFTLibrary);
 
+    Factory::SetFTLibrary(gPlatformFTLibrary);
+
     RegisterStrongMemoryReporter(new FreetypeReporter());
 
     mOffscreenFormat = GetScreenDepth() == 16
                        ? SurfaceFormat::R5G6B5_UINT16
                        : SurfaceFormat::X8R8G8B8_UINT32;
 
     if (gfxPrefs::AndroidRGB16Force()) {
         mOffscreenFormat = SurfaceFormat::R5G6B5_UINT16;
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -80,18 +80,16 @@ public:
     {
         if (aFontEntry->mFTFace) {
             mFace = aFontEntry->mFTFace;
             return;
         }
 
         NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
                      "can't use AutoFTFace for fonts without a filename");
-        FT_Library ft = gfxPlatform::GetPlatform()->GetFTLibrary();
-        MOZ_ASSERT(ft);
 
         // A relative path (no initial "/") means this is a resource in
         // omnijar, not an installed font on the device.
         // The NS_ASSERTIONs here should never fail, as the resource must have
         // been read successfully during font-list initialization or we'd never
         // have created the font entry. The only legitimate runtime failure
         // here would be memory allocation, in which case mFace remains null.
         if (aFontEntry->mFilename[0] != '/') {
@@ -103,37 +101,36 @@ public:
             uint32_t bufSize = item->RealSize();
             mFontDataBuf = static_cast<uint8_t*>(malloc(bufSize));
             if (mFontDataBuf) {
                 nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
                 cursor.Copy(&bufSize);
                 NS_ASSERTION(bufSize == item->RealSize(),
                              "error reading bundled font");
 
-                if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize,
-                                                    aFontEntry->mFTFontIndex,
-                                                    &mFace)) {
+                mFace = Factory::NewFTFaceFromData(nullptr, mFontDataBuf, bufSize, aFontEntry->mFTFontIndex);
+                if (!mFace) {
                     NS_WARNING("failed to create freetype face");
                 }
             }
         } else {
-            if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(),
-                                         aFontEntry->mFTFontIndex, &mFace)) {
+            mFace = Factory::NewFTFace(nullptr, aFontEntry->mFilename.get(), aFontEntry->mFTFontIndex);
+            if (!mFace) {
                 NS_WARNING("failed to create freetype face");
             }
         }
         if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
             NS_WARNING("failed to select Unicode charmap");
         }
         mOwnsFace = true;
     }
 
     ~AutoFTFace() {
         if (mFace && mOwnsFace) {
-            FT_Done_Face(mFace);
+            Factory::ReleaseFTFace(mFace);
             if (mFontDataBuf) {
                 free(mFontDataBuf);
             }
         }
     }
 
     operator FT_Face() { return mFace; }
 
@@ -263,26 +260,23 @@ FT2FontEntry::CreateFontEntry(const nsAS
                               int16_t aStretch,
                               uint8_t aStyle,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
     // Ownership of aFontData is passed in here; the fontEntry must
     // retain it as long as the FT_Face needs it, and ensure it is
     // eventually deleted.
-    FT_Face face;
-    FT_Error error =
-        FT_New_Memory_Face(gfxPlatform::GetPlatform()->GetFTLibrary(),
-                           aFontData, aLength, 0, &face);
-    if (error != FT_Err_Ok) {
+    FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
+    if (!face) {
         free((void*)aFontData);
         return nullptr;
     }
     if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
-        FT_Done_Face(face);
+        Factory::ReleaseFTFace(face);
         free((void*)aFontData);
         return nullptr;
     }
     // Create our FT2FontEntry, which inherits the name of the userfont entry
     // as it's not guaranteed that the face has valid names (bug 737315)
     FT2FontEntry* fe =
         FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
                                       aFontData);
@@ -299,17 +293,17 @@ class FTUserFontData {
 public:
     FTUserFontData(FT_Face aFace, const uint8_t* aData)
         : mFace(aFace), mFontData(aData)
     {
     }
 
     ~FTUserFontData()
     {
-        FT_Done_Face(mFace);
+        Factory::ReleaseFTFace(mFace);
         if (mFontData) {
             free((void*)mFontData);
         }
     }
 
     const uint8_t *FontData() const { return mFontData; }
 
 private:
@@ -983,32 +977,31 @@ gfxFT2FontList::AppendFacesFromFontFile(
         s.st_mtime == timestamp && s.st_size == filesize)
     {
         LOG(("using cached font info for %s", aFileName.get()));
         AppendFacesFromCachedFaceList(aFileName, cachedFaceList, aStdFile,
                                       aVisibility);
         return;
     }
 
-    FT_Library ftLibrary = gfxPlatform::GetPlatform()->GetFTLibrary();
-    FT_Face dummy;
-    if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
+    FT_Face dummy = Factory::NewFTFace(nullptr, aFileName.get(), -1);
+    if (dummy) {
         LOG(("reading font info via FreeType for %s", aFileName.get()));
         nsCString newFaceList;
         timestamp = s.st_mtime;
         filesize = s.st_size;
         for (FT_Long i = 0; i < dummy->num_faces; i++) {
-            FT_Face face;
-            if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
+            FT_Face face = Factory::NewFTFace(nullptr, aFileName.get(), i);
+            if (!face) {
                 continue;
             }
             AddFaceToList(aFileName, i, aStdFile, aVisibility, face, newFaceList);
-            FT_Done_Face(face);
+            Factory::ReleaseFTFace(face);
         }
-        FT_Done_Face(dummy);
+        Factory::ReleaseFTFace(dummy);
         if (aCache && 0 == statRetval && !newFaceList.IsEmpty()) {
             aCache->CacheFileInfo(aFileName, newFaceList, timestamp, filesize);
         }
     }
 }
 
 void
 gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
@@ -1135,34 +1128,32 @@ gfxFT2FontList::AppendFacesFromOmnijarEn
     nsZipCursor cursor(item, aArchive, buf.get(), bufSize);
     uint8_t* data = cursor.Copy(&bufSize);
     NS_ASSERTION(data && bufSize == item->RealSize(),
                  "error reading bundled font");
     if (!data) {
         return;
     }
 
-    FT_Library ftLibrary = gfxPlatform::GetPlatform()->GetFTLibrary();
-
-    FT_Face dummy;
-    if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, 0, &dummy)) {
+    FT_Face dummy = Factory::NewFTFaceFromData(nullptr, buf.get(), bufSize, 0);
+    if (!dummy) {
         return;
     }
 
     for (FT_Long i = 0; i < dummy->num_faces; i++) {
-        FT_Face face;
-        if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, i, &face)) {
+        FT_Face face = Factory::NewFTFaceFromData(nullptr, buf.get(), bufSize, i);
+        if (!face) {
             continue;
         }
         AddFaceToList(aEntryName, i, kStandard, FT2FontFamily::kVisible,
                       face, faceList);
-        FT_Done_Face(face);
+        Factory::ReleaseFTFace(face);
     }
 
-    FT_Done_Face(dummy);
+    Factory::ReleaseFTFace(dummy);
 
     if (aCache && !faceList.IsEmpty()) {
         aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
     }
 }
 
 // Called on each family after all fonts are added to the list;
 // this will sort faces to give priority to "standard" font files
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -443,17 +443,17 @@ gfxFontconfigFontEntry::MaybeReleaseFTFa
 {
     // don't release if either HB or Gr face still exists
     if (mHBFace || mGrFace) {
         return;
     }
     // only close out FT_Face for system fonts, not for data fonts
     if (!mIsDataUserFont) {
         if (mFTFace) {
-            FT_Done_Face(mFTFace);
+            Factory::ReleaseFTFace(mFTFace);
             mFTFace = nullptr;
         }
         mFTFaceInitialized = false;
     }
 }
 
 void
 gfxFontconfigFontEntry::ForgetHBFace()
@@ -647,16 +647,22 @@ PrepareFontOptions(FcPattern* aPattern,
     } else if (rgba == FC_RGBA_NONE) {
         antialias = CAIRO_ANTIALIAS_GRAY;
     } else {
         antialias = CAIRO_ANTIALIAS_SUBPIXEL;
     }
     cairo_font_options_set_antialias(aFontOptions, antialias);
 }
 
+static void
+ReleaseFTUserFontData(void* aData)
+{
+  static_cast<FTUserFontData*>(aData)->Release();
+}
+
 cairo_scaled_font_t*
 gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
                                          gfxFloat aAdjustedSize,
                                          const gfxFontStyle *aStyle,
                                          bool aNeedsBold)
 {
     if (aNeedsBold) {
         FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
@@ -676,20 +682,25 @@ gfxFontconfigFontEntry::CreateScaledFont
     cairo_font_face_t *face =
         cairo_ft_font_face_create_for_pattern(aRenderPattern);
 
     if (mFontData) {
         // for data fonts, add the face/data pointer to the cairo font face
         // so that it gets deleted whenever cairo decides
         NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
         NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
-        cairo_font_face_set_user_data(face,
-                                      &sFcFontlistUserFontDataKey,
-                                      new FTUserFontDataRef(mUserFontData),
-                                      FTUserFontDataRef::Destroy);
+        if (cairo_font_face_set_user_data(face,
+                                          &sFcFontlistUserFontDataKey,
+                                          mUserFontData,
+                                          ReleaseFTUserFontData) != CAIRO_STATUS_SUCCESS) {
+            NS_WARNING("Failed binding FTUserFontData to Cairo font face");
+            cairo_font_face_destroy(face);
+            return nullptr;
+        }
+        mUserFontData.get()->AddRef();
     }
 
     cairo_scaled_font_t *scaledFont = nullptr;
 
     cairo_matrix_t sizeMatrix;
     cairo_matrix_t identityMatrix;
 
     cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize);
@@ -928,18 +939,18 @@ gfxFontconfigFontEntry::CopyFontTable(ui
         FcChar8 *filename;
         if (FcPatternGetString(mFontPattern, FC_FILE, 0, &filename) != FcResultMatch) {
             return NS_ERROR_FAILURE;
         }
         int index;
         if (FcPatternGetInteger(mFontPattern, FC_INDEX, 0, &index) != FcResultMatch) {
             index = 0; // default to 0 if not found in pattern
         }
-        if (FT_New_Face(gfxFcPlatformFontList::GetFTLibrary(),
-                        (const char*)filename, index, &mFTFace) != 0) {
+        mFTFace = Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
+        if (!mFTFace) {
             return NS_ERROR_FAILURE;
         }
     }
 
     if (!mFTFace) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
@@ -1428,26 +1439,23 @@ gfxFcPlatformFontList::LookupLocalFont(c
 gfxFontEntry*
 gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
                                         uint16_t aWeight,
                                         int16_t aStretch,
                                         uint8_t aStyle,
                                         const uint8_t* aFontData,
                                         uint32_t aLength)
 {
-    FT_Face face;
-    FT_Error error =
-        FT_New_Memory_Face(gfxFcPlatformFontList::GetFTLibrary(),
-                           aFontData, aLength, 0, &face);
-    if (error != FT_Err_Ok) {
+    FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
+    if (!face) {
         NS_Free((void*)aFontData);
         return nullptr;
     }
     if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
-        FT_Done_Face(face);
+        Factory::ReleaseFTFace(face);
         NS_Free((void*)aFontData);
         return nullptr;
     }
 
     return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
                                       aStyle, aFontData, face);
 }
 
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -41,55 +41,38 @@ public:
 // face is destroyed. Since multiple faces may use the same data, be
 // careful to assure that the data is only cleared out when all uses
 // expire. The font entry object contains a refptr to FTUserFontData and
 // each cairo font created from that font entry contains a
 // FTUserFontDataRef with a refptr to that same FTUserFontData object.
 
 class FTUserFontData {
 public:
-    NS_INLINE_DECL_REFCOUNTING(FTUserFontData)
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FTUserFontData)
 
     explicit FTUserFontData(FT_Face aFace, const uint8_t* aData)
         : mFace(aFace), mFontData(aData)
     {
     }
 
     const uint8_t *FontData() const { return mFontData; }
 
 private:
     ~FTUserFontData()
     {
-        FT_Done_Face(mFace);
+        mozilla::gfx::Factory::ReleaseFTFace(mFace);
         if (mFontData) {
             NS_Free((void*)mFontData);
         }
     }
 
     FT_Face        mFace;
     const uint8_t *mFontData;
 };
 
-class FTUserFontDataRef {
-public:
-    explicit FTUserFontDataRef(FTUserFontData *aUserFontData)
-        : mUserFontData(aUserFontData)
-    {
-    }
-
-    static void Destroy(void* aData) {
-        FTUserFontDataRef* aUserFontDataRef =
-            static_cast<FTUserFontDataRef*>(aData);
-        delete aUserFontDataRef;
-    }
-
-private:
-    RefPtr<FTUserFontData> mUserFontData;
-};
-
 // The names for the font entry and font classes should really
 // the common 'Fc' abbreviation but the gfxPangoFontGroup code already
 // defines versions of these, so use the verbose name for now.
 
 class gfxFontconfigFontEntry : public gfxFontEntry {
 public:
     // used for system fonts with explicit patterns
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,