
# HG changeset patch
# User Jonathan Kew <jfkthame@gmail.com>
# Date 1251640524 -3600
# Node ID 735bf992c2e4e901a3aafdb37155c20c26c3a606
# Parent c8ca022e598394b8b38b77e09fc4bb296c286d36
implement support for WOFF fonts via @font-face

diff --git a/gfx/thebes/public/gfxFT2Fonts.h b/gfx/thebes/public/gfxFT2Fonts.h
--- a/gfx/thebes/public/gfxFT2Fonts.h
+++ b/gfx/thebes/public/gfxFT2Fonts.h
@@ -81,22 +81,24 @@ public:
     FontEntry(const FontEntry& aFontEntry);
     ~FontEntry();
 
     const nsString& GetName() const {
         return mFaceName;
     }
 
     static FontEntry* 
-    CreateFontEntry(const gfxProxyFontEntry &aProxyEntry, nsISupports *aLoader,
+    CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
                     const PRUint8 *aFontData, PRUint32 aLength);
-    
+
     static FontEntry* 
-    CreateFontEntryFromFace(FT_Face aFace);
-    
+    CreateFontEntryFromFace(FT_Face aFace, const PRUint8 *aFontData = nsnull);
+        // aFontData is NS_Malloc'ed data that aFace depends on, to be freed
+        // after the face is destroyed; null if there is no such buffer
+
     cairo_font_face_t *CairoFontFace();
     nsresult ReadCMAP();
 
     FT_Face mFTFace;
     cairo_font_face_t *mFontFace;
 
     nsString mFaceName;
     nsCString mFilename;
diff --git a/gfx/thebes/public/gfxFontUtils.h b/gfx/thebes/public/gfxFontUtils.h
--- a/gfx/thebes/public/gfxFontUtils.h
+++ b/gfx/thebes/public/gfxFontUtils.h
@@ -354,16 +354,23 @@ struct AutoSwap_PRUint64 {
 // used for overlaying name changes without touching original font data
 struct FontDataOverlay {
     // overlaySrc != 0 ==> use overlay
     PRUint32  overlaySrc;    // src offset from start of font data
     PRUint32  overlaySrcLen; // src length
     PRUint32  overlayDest;   // dest offset from start of font data
 };
     
+enum gfxUserFontType {
+    GFX_USERFONT_UNKNOWN = 0,
+    GFX_USERFONT_OPENTYPE = 1,
+    GFX_USERFONT_SVG = 2,
+    GFX_USERFONT_WOFF = 3
+};
+
 class THEBES_API gfxFontUtils {
 
 public:
     // these are public because gfxFont.cpp also looks into the name table
     enum {
         NAME_ID_FAMILY = 1,
         NAME_ID_STYLE = 2,
         NAME_ID_UNIQUE = 3,
@@ -479,23 +486,32 @@ public:
     // given a TrueType/OpenType data file, produce a EOT-format header
     // for use with Windows T2Embed API AddFontResource type API's
     // effectively hide existing fonts with matching names aHeaderLen is
     // the size of the header buffer on input, the actual size of the
     // EOT header on output
     static nsresult
     MakeEOTHeader(const PRUint8 *aFontData, PRUint32 aFontDataLength,
                   nsTArray<PRUint8> *aHeader, FontDataOverlay *aOverlay);
+
+    // determine whether a font (which has already passed ValidateSFNTHeaders)
+    // is CFF format rather than TrueType
+    static PRBool
+    IsCffFont(const PRUint8* aFontData);
+
 #endif
 
+    // determine the format of font data
+    static gfxUserFontType
+    DetermineFontDataType(const PRUint8 *aFontData, PRUint32 aFontDataLength);
+
     // checks for valid SFNT table structure, returns true if valid
     // does *not* guarantee that all font data is valid
     static PRBool
-    ValidateSFNTHeaders(const PRUint8 *aFontData, PRUint32 aFontDataLength,
-                        PRBool *aIsCFF = nsnull);
+    ValidateSFNTHeaders(const PRUint8 *aFontData, PRUint32 aFontDataLength);
     
     // create a new name table and build a new font with that name table
     // appended on the end, returns true on success
     static nsresult
     RenameFont(const nsAString& aName, const PRUint8 *aFontData, 
                PRUint32 aFontDataLength, nsTArray<PRUint8> *aNewFont);
     
     // read all names matching aNameID, returning in aNames array
diff --git a/gfx/thebes/public/gfxPangoFonts.h b/gfx/thebes/public/gfxPangoFonts.h
--- a/gfx/thebes/public/gfxPangoFonts.h
+++ b/gfx/thebes/public/gfxPangoFonts.h
@@ -83,17 +83,16 @@ public:
 
     static void Shutdown();
 
     // Used for @font-face { src: local(); }
     static gfxFontEntry *NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
                                       const nsAString &aFullname);
     // Used for @font-face { src: url(); }
     static gfxFontEntry *NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                                      nsISupports *aLoader,
                                       const PRUint8 *aFontData,
                                       PRUint32 aLength);
 
     // Interfaces used internally
     // (but public so that they can be accessed from non-member functions):
 
     // The FontGroup holds the reference to the PangoFont (through the FontSet).
     PangoFont *GetBasePangoFont();
diff --git a/gfx/thebes/public/gfxPlatform.h b/gfx/thebes/public/gfxPlatform.h
--- a/gfx/thebes/public/gfxPlatform.h
+++ b/gfx/thebes/public/gfxPlatform.h
@@ -206,25 +206,25 @@ public:
      * who must either AddRef() or delete.
      */
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName)
     { return nsnull; }
 
     /**
      * Activate a platform font.  (Needed to support @font-face src url().)
-     * aFontData must persist as long as a reference is held to aLoader.
+     * aFontData is a NS_Malloc'ed block that must be freed by this function
+     * (or responsibility passed on) when it is no longer needed; the caller
+     * will NOT free it.
      * Ownership of the returned gfxFontEntry is passed to the caller,
      * who must either AddRef() or delete.
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
                                            const PRUint8 *aFontData,
-                                           PRUint32 aLength)
-    { return nsnull; }
+                                           PRUint32 aLength);
 
     /**
      * Whether to allow downloadable fonts via @font-face rules
      */
     virtual PRBool DownloadableFontsEnabled();
 
     // check whether format is supported on a platform or not (if unclear, returns true)
     virtual PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags) { return PR_FALSE; }
diff --git a/gfx/thebes/public/gfxPlatformGtk.h b/gfx/thebes/public/gfxPlatformGtk.h
--- a/gfx/thebes/public/gfxPlatformGtk.h
+++ b/gfx/thebes/public/gfxPlatformGtk.h
@@ -97,17 +97,16 @@ public:
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
 
     /**
      * Activate a platform font (needed to support @font-face src url() )
      *
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
                                            const PRUint8 *aFontData,
                                            PRUint32 aLength);
 
     /**
      * Check whether format is supported on a platform or not (if unclear,
      * returns true).
      */
     virtual PRBool IsFontFormatSupported(nsIURI *aFontURI,
diff --git a/gfx/thebes/public/gfxPlatformMac.h b/gfx/thebes/public/gfxPlatformMac.h
--- a/gfx/thebes/public/gfxPlatformMac.h
+++ b/gfx/thebes/public/gfxPlatformMac.h
@@ -73,17 +73,16 @@ public:
                                   gfxUserFontSet *aUserFontSet);
 
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
 
     virtual gfxPlatformFontList* CreatePlatformFontList();
 
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
                                            const PRUint8 *aFontData,
                                            PRUint32 aLength);
 
     PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags);
 
     nsresult GetFontList(const nsACString& aLangGroup,
                          const nsACString& aGenericFamily,
                          nsTArray<nsString>& aListOfFonts);
diff --git a/gfx/thebes/public/gfxUserFontSet.h b/gfx/thebes/public/gfxUserFontSet.h
--- a/gfx/thebes/public/gfxUserFontSet.h
+++ b/gfx/thebes/public/gfxUserFontSet.h
@@ -192,17 +192,19 @@ public:
     // completion will call OnLoadComplete method
     virtual nsresult StartLoad(gfxFontEntry *aFontToLoad, 
                                const gfxFontFaceSrc *aFontFaceSrc) = 0;
 
     // when download has been completed, pass back data here
     // aDownloadStatus == NS_OK ==> download succeeded, error otherwise
     // returns true if platform font creation sucessful (or local()
     // reference was next in line)
-    PRBool OnLoadComplete(gfxFontEntry *aFontToLoad, nsISupports *aLoader,
+    // Ownership of aFontData is passed in here; the font set must
+    // ensure that it is eventually deleted with NS_Free().
+    PRBool OnLoadComplete(gfxFontEntry *aFontToLoad,
                           const PRUint8 *aFontData, PRUint32 aLength,
                           nsresult aDownloadStatus);
 
     // generation - each time a face is loaded, generation is
     // incremented so that the change can be recognized 
     PRUint64 GetGeneration() { return mGeneration; }
 
 protected:
diff --git a/gfx/thebes/public/gfxWindowsFonts.h b/gfx/thebes/public/gfxWindowsFonts.h
--- a/gfx/thebes/public/gfxWindowsFonts.h
+++ b/gfx/thebes/public/gfxWindowsFonts.h
@@ -135,17 +135,16 @@ public:
         mUnicodeRanges(aFontEntry.mUnicodeRanges)
     {
 
     }
     static void InitializeFontEmbeddingProcs();
 
     // create a font entry from downloaded font data
     static FontEntry* LoadFont(const gfxProxyFontEntry &aProxyEntry,
-                               nsISupports *aLoader,
                                const PRUint8 *aFontData,
                                PRUint32 aLength);
 
     // create a font entry for a font with a given name
     static FontEntry* CreateFontEntry(const nsAString& aName, 
                                       gfxWindowsFontType aFontType, 
                                       PRBool aItalic, PRUint16 aWeight, 
                                       gfxUserFontData* aUserFontData, 
diff --git a/gfx/thebes/public/gfxWindowsPlatform.h b/gfx/thebes/public/gfxWindowsPlatform.h
--- a/gfx/thebes/public/gfxWindowsPlatform.h
+++ b/gfx/thebes/public/gfxWindowsPlatform.h
@@ -121,17 +121,16 @@ public:
      */
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName);
 
     /**
      * Activate a platform font (needed to support @font-face src url() )
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                           nsISupports *aLoader,
                                            const PRUint8 *aFontData,
                                            PRUint32 aLength);
 
     /**
      * Check whether format is supported on a platform or not (if unclear, returns true)
      */
     virtual PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags);
 
diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in
--- a/gfx/thebes/src/Makefile.in
+++ b/gfx/thebes/src/Makefile.in
@@ -149,19 +149,21 @@ CPPSRCS += gfxCoreTextFonts.cpp
 endif
 
 CMMSRCS = gfxMacPlatformFontList.mm
 
 # Always link with OpenGL/AGL
 EXTRA_DSO_LDOPTS += -framework OpenGL -framework AGL -framework QuickTime
 endif
 
+CSRCS += woff.c
+
 EXTRA_DSO_LDOPTS += $(TK_LIBS)
 
-DEFINES += -DIMPL_THEBES
+DEFINES += -DIMPL_THEBES -DWOFF_MOZILLA_CLIENT
 
 include $(topsrcdir)/config/rules.mk
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(TK_CFLAGS)
 CFLAGS += $(MOZ_CAIRO_CFLAGS) $(TK_CFLAGS)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 ifdef WINCE
diff --git a/gfx/thebes/src/gfxFT2Fonts.cpp b/gfx/thebes/src/gfxFT2Fonts.cpp
--- a/gfx/thebes/src/gfxFT2Fonts.cpp
+++ b/gfx/thebes/src/gfxFT2Fonts.cpp
@@ -112,43 +112,66 @@ FontEntry::~FontEntry()
         cairo_font_face_destroy(mFontFace);
         mFontFace = nsnull;
     }
 }
 
 /* static */
 FontEntry*
 FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                           nsISupports *aLoader, const PRUint8 *aFontData,
-                           PRUint32 aLength) {
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength))
-        return nsnull;
+                           const PRUint8 *aFontData,
+                           PRUint32 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(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
                            aFontData, aLength, 0, &face);
-    if (error != FT_Err_Ok)
+    if (error != FT_Err_Ok) {
+        NS_Free((void*)aFontData);
         return nsnull;
-    FontEntry* fe = FontEntry::CreateFontEntryFromFace(face);
+    }
+    FontEntry* fe = FontEntry::CreateFontEntryFromFace(face, aFontData);
     fe->mItalic = aProxyEntry.mItalic;
     fe->mWeight = aProxyEntry.mWeight;
     fe->mStretch = aProxyEntry.mStretch;
     return fe;
+}
 
-}
+class FTUserFontData {
+public:
+    FTUserFontData(FT_Face aFace, const PRUint8* aData)
+        : mFace(aFace), mFontData(aData)
+    {
+    }
+
+    ~FTUserFontData()
+    {
+        FT_Done_Face(mFace);
+        if (mFontData) {
+            NS_Free((void*)mFontData);
+        }
+    }
+
+private:
+    FT_Face        mFace;
+    const PRUint8 *mFontData;
+};
 
 static void
 FTFontDestroyFunc(void *data)
 {
-    FT_Face face = (FT_Face)data;
-    FT_Done_Face(face);
+    FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
+    delete userFontData;
 }
 
 /* static */ FontEntry*
-FontEntry::CreateFontEntryFromFace(FT_Face aFace) {
+FontEntry::CreateFontEntryFromFace(FT_Face aFace, const PRUint8 *aFontData) {
     static cairo_user_data_key_t key;
 
     if (!aFace->family_name) {
         FT_Done_Face(aFace);
         return nsnull;
     }
     // Construct font name from family name and style name, regular fonts
     // do not have the modifier by convention.
@@ -156,20 +179,22 @@ FontEntry::CreateFontEntryFromFace(FT_Fa
     if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
         fontName.AppendLiteral(" ");
         AppendUTF8toUTF16(aFace->style_name, fontName);
     }
     FontEntry *fe = new FontEntry(fontName);
     fe->mItalic = aFace->style_flags & FT_STYLE_FLAG_ITALIC;
     fe->mFTFace = aFace;
     fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, 0);
+
+    FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
     cairo_font_face_set_user_data(fe->mFontFace, &key,
-                                  aFace, FTFontDestroyFunc);
+                                  userFontData, FTFontDestroyFunc);
+
     TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
-
     PRUint16 os2weight = 0;
     if (os2 && os2->version != 0xffff) {
         // Technically, only 100 to 900 are valid, but some fonts
         // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
         // it set to 6 instead of 600.  We try to be nice and handle that
         // as well.
         if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900)
             os2weight = os2->usWeightClass;
@@ -200,17 +225,19 @@ FontEntry::CairoFontFace()
 {
     static cairo_user_data_key_t key;
 
     if (!mFontFace) {
         FT_Face face;
         FT_New_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(), mFilename.get(), mFTFontIndex, &face);
         mFTFace = face;
         mFontFace = cairo_ft_font_face_create_for_ft_face(face, 0);
-        cairo_font_face_set_user_data(mFontFace, &key, face, FTFontDestroyFunc);
+        FTUserFontData *userFontData = new FTUserFontData(face, nsnull);
+        cairo_font_face_set_user_data(mFontFace, &key,
+                                      userFontData, FTFontDestroyFunc);
     }
     return mFontFace;
 }
 
 nsresult
 FontEntry::ReadCMAP()
 {
     if (mCmapInitialized) return NS_OK;
diff --git a/gfx/thebes/src/gfxFontUtils.cpp b/gfx/thebes/src/gfxFontUtils.cpp
--- a/gfx/thebes/src/gfxFontUtils.cpp
+++ b/gfx/thebes/src/gfxFontUtils.cpp
@@ -47,16 +47,18 @@
 #include "nsISupportsPrimitives.h"
 #include "nsIStreamBufferAccess.h"
 #include "nsIUUIDGenerator.h"
 #include "nsMemory.h"
 #include "nsICharsetConverterManager.h"
 
 #include "plbase64.h"
 
+#include "woff.h"
+
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
 #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
 
 /* Unicode subrange table
  *   from: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/unicode_63ub.asp
@@ -689,41 +691,64 @@ ValidateKernTable(const PRUint8 *aKernTa
         // for now, assume this is OK
         return PR_TRUE;
     }
 
     // neither the old Windows version nor the newer Apple one; refuse to use it
     return PR_FALSE;
 }
 
+gfxUserFontType
+gfxFontUtils::DetermineFontDataType(const PRUint8 *aFontData, PRUint32 aFontDataLength)
+{
+    // test for OpenType font data
+    // problem: EOT-Lite with 0x10000 length will look like TrueType!
+    if (aFontDataLength >= sizeof(SFNTHeader)) {
+        const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+        PRUint32 sfntVersion = sfntHeader->sfntVersion;
+        if (IsValidSFNTVersion(sfntVersion)) {
+            return GFX_USERFONT_OPENTYPE;
+        }
+    }
+    
+    // test for WOFF
+    if (aFontDataLength >= sizeof(AutoSwap_PRUint32)) {
+        const AutoSwap_PRUint32 *version = 
+            reinterpret_cast<const AutoSwap_PRUint32*>(aFontData);
+        if (PRUint32(*version) == TRUETYPE_TAG('w','O','F','F')) {
+            return GFX_USERFONT_WOFF;
+        }
+    }
+    
+    // tests for other formats here
+    
+    return GFX_USERFONT_UNKNOWN;
+}
+
 PRBool
 gfxFontUtils::ValidateSFNTHeaders(const PRUint8 *aFontData, 
-                                  PRUint32 aFontDataLength, 
-                                  PRBool *aIsCFF)
+                                  PRUint32 aFontDataLength)
 {
-    NS_ASSERTION(aFontData && aFontDataLength != 0, "null font data");
+    NS_ASSERTION(aFontData, "null font data");
 
     PRUint64 dataLength(aFontDataLength);
     
     // read in the sfnt header
     if (sizeof(SFNTHeader) > aFontDataLength) {
         NS_WARNING("invalid font (insufficient data)");
         return PR_FALSE;
     }
     
     const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
     PRUint32 sfntVersion = sfntHeader->sfntVersion;
     if (!IsValidSFNTVersion(sfntVersion)) {
         NS_WARNING("invalid font (SFNT version)");
         return PR_FALSE;
     }
     
-    if (aIsCFF)
-        *aIsCFF = (sfntVersion == TRUETYPE_TAG('O','T','T','O'));
-
     // iterate through the table headers to find the head, name and OS/2 tables
     PRBool foundHead = PR_FALSE, foundOS2 = PR_FALSE, foundName = PR_FALSE;
     PRBool foundGlyphs = PR_FALSE, foundCFF = PR_FALSE, foundKern = PR_FALSE;
     PRUint32 headOffset, headLen, nameOffset, nameLen, kernOffset, kernLen;
     PRUint32 i, numTables;
 
     numTables = sfntHeader->numTables;
     PRUint32 headerLen = sizeof(SFNTHeader) + sizeof(TableDirEntry) * numTables;
@@ -1728,9 +1753,19 @@ gfxFontUtils::MakeEOTHeader(const PRUint
 
     eotHeader->eotSize = aHeader->Length() + fontDataSize;
 
     // DumpEOTHeader(aHeader->Elements(), aHeader->Length());
 
     return NS_OK;
 }
 
+/* static */
+PRBool
+IsCffFont(const PRUint8* aFontData)
+{
+    // this is only called after aFontData has passed basic validation,
+    // so we know there is enough data present to allow us to read the version!
+    const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+    return (sfntHeader->sfntVersion == TRUETYPE_TAG('O','T','T','O'));
+}
+
 #endif
diff --git a/gfx/thebes/src/gfxMacPlatformFontList.mm b/gfx/thebes/src/gfxMacPlatformFontList.mm
--- a/gfx/thebes/src/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/src/gfxMacPlatformFontList.mm
@@ -685,47 +685,33 @@ public:
         if (mContainerRef)
             ::ATSFontDeactivate(mContainerRef, NULL, kATSOptionFlagsDefault);
     }
 
     ATSFontContainerRef     mContainerRef;
 };
 
 gfxFontEntry* 
-gfxMacPlatformFontList::MakePlatformFont(const gfxFontEntry *aProxyEntry, 
-                                     const PRUint8 *aFontData, PRUint32 aLength)
+gfxMacPlatformFontList::MakePlatformFont(const gfxFontEntry *aProxyEntry,
+                                         const PRUint8 *aFontData,
+                                         PRUint32 aLength)
 {
     OSStatus err;
     
-    NS_ASSERTION(aFontData && aLength != 0, 
-                 "MakePlatformFont called with null data ptr");
-                 
-    // do simple validation check on font data before 
-    // attempting to activate it
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
-#if DEBUG
-        char warnBuf[1024];
-        const gfxProxyFontEntry *proxyEntry = 
-            static_cast<const gfxProxyFontEntry*> (aProxyEntry);
-        sprintf(warnBuf, "downloaded font error, invalid font data for (%s)",
-                NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
-        NS_WARNING(warnBuf);
-#endif    
-        return nsnull;
-    }
+    NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
 
     ATSFontRef fontRef;
     ATSFontContainerRef containerRef;
 
     // we get occasional failures when multiple fonts are activated in quick succession
     // if the ATS font cache is damaged; to work around this, we can retry the activation
     const PRUint32 kMaxRetries = 3;
     PRUint32 retryCount = 0;
     while (retryCount++ < kMaxRetries) {
-        err = ::ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength, 
+        err = ::ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength,
                                           kPrivateATSFontContextPrivate,
                                           kATSFontFormatUnspecified,
                                           NULL, 
                                           kATSOptionFlagsDoNotNotify, 
                                           &containerRef);
         mATSGeneration = ::ATSGetGeneration();
 
         if (err != noErr) {
diff --git a/gfx/thebes/src/gfxPangoFonts.cpp b/gfx/thebes/src/gfxPangoFonts.cpp
--- a/gfx/thebes/src/gfxPangoFonts.cpp
+++ b/gfx/thebes/src/gfxPangoFonts.cpp
@@ -295,38 +295,42 @@ public:
 /**
  * gfxDownloadedFcFontEntry:
  *
  * An implementation of gfxFcFontEntry for web fonts from src:url().
  */
 
 class gfxDownloadedFcFontEntry : public gfxFcFontEntry {
 public:
-    // This takes ownership of the face.
+    // This takes ownership of the face and its underlying data
     gfxDownloadedFcFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                             nsISupports *aLoader, FT_Face aFace)
-        : gfxFcFontEntry(aProxyEntry), mLoader(aLoader), mFace(aFace)
+                             const PRUint8 *aData, FT_Face aFace)
+        : gfxFcFontEntry(aProxyEntry), mFontData(aData), mFace(aFace)
     {
         NS_PRECONDITION(aFace != NULL, "aFace is NULL!");
         InitPattern();
     }
 
     virtual ~gfxDownloadedFcFontEntry();
 
     // Returns a PangoCoverage owned by the FontEntry.  The caller must add a
     // reference if it wishes to keep the PangoCoverage longer than the
     // lifetime of the FontEntry.
     PangoCoverage *GetPangoCoverage();
 
 protected:
     virtual void InitPattern();
 
-    // mLoader holds a reference to memory used by mFace.
-    nsCOMPtr<nsISupports> mLoader;
+    // mFontData holds the data used to instantiate the FT_Face;
+    // this has to persist until we are finished with the face,
+    // then be released with NS_Free().
+    const PRUint8* mFontData;
+
     FT_Face mFace;
+
     // mPangoCoverage is the charset property of the pattern translated to a
     // format that Pango understands.  A reference is kept here so that it can
     // be shared by multiple PangoFonts (of different sizes).
     nsAutoRef<PangoCoverage> mPangoCoverage;
 };
 
 // A property for recording gfxDownloadedFcFontEntrys on FcPatterns.
 static const char *kFontEntryFcProp = "-moz-font-entry";
@@ -366,16 +370,17 @@ gfxDownloadedFcFontEntry::~gfxDownloaded
         // Remove back reference to this font entry and the face in case
         // anyone holds a reference to the pattern.
         NS_ASSERTION(mPatterns.Length() == 1,
                      "More than one pattern in gfxDownloadedFcFontEntry!");
         DelDownloadedFontEntry(mPatterns[0]);
         FcPatternDel(mPatterns[0], FC_FT_FACE);
     }
     FT_Done_Face(mFace);
+    NS_Free((void*)mFontData);
 }
 
 typedef FcPattern* (*QueryFaceFunction)(const FT_Face face,
                                         const FcChar8 *file, int id,
                                         FcBlanks *blanks);
 
 void
 gfxDownloadedFcFontEntry::InitPattern()
@@ -2213,28 +2218,32 @@ GetFTLibrary()
         gFTLibrary = face.get()->glyph->library;
     }
 
     return gFTLibrary;
 }
 
 /* static */ gfxFontEntry *
 gfxPangoFontGroup::NewFontEntry(const gfxProxyFontEntry &aProxyEntry,
-                                nsISupports *aLoader,
                                 const PRUint8 *aFontData, PRUint32 aLength)
 {
+    // Ownership of aFontData is passed in here, and transferred to the
+    // new fontEntry, which will release it when no longer needed.
+
     // Using face_index = 0 for the first face in the font, as we have no
     // other information.  FT_New_Memory_Face checks for a NULL FT_Library.
     FT_Face face;
     FT_Error error =
         FT_New_Memory_Face(GetFTLibrary(), aFontData, aLength, 0, &face);
-    if (error != 0)
+    if (error != 0) {
+        NS_Free((void*)aFontData);
         return nsnull;
+    }
 
-    return new gfxDownloadedFcFontEntry(aProxyEntry, aLoader, face);
+    return new gfxDownloadedFcFontEntry(aProxyEntry, aFontData, face);
 }
 
 
 static double
 GetPixelSize(FcPattern *aPattern)
 {
     double size;
     if (FcPatternGetDouble(aPattern,
diff --git a/gfx/thebes/src/gfxPlatform.cpp b/gfx/thebes/src/gfxPlatform.cpp
--- a/gfx/thebes/src/gfxPlatform.cpp
+++ b/gfx/thebes/src/gfxPlatform.cpp
@@ -322,16 +322,31 @@ gfxPlatform::DownloadableFontsEnabled()
             if (NS_SUCCEEDED(rv))
                 allowDownloadableFonts = allow;
         }
     }
 
     return allowDownloadableFonts;
 }
 
+gfxFontEntry*
+gfxPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
+                              const PRUint8 *aFontData,
+                              PRUint32 aLength)
+{
+    // Default implementation does not handle activating downloaded fonts;
+    // just free the data and return.
+    // Platforms that support @font-face must override this,
+    // using the data to instantiate the font, and taking responsibility
+    // for freeing it when no longer required.
+    if (aFontData) {
+        NS_Free((void*)aFontData);
+    }
+    return nsnull;
+}
 
 static void
 AppendGenericFontFromPref(nsString& aFonts, const char *aLangGroup, const char *aGenericName)
 {
     nsresult rv;
 
     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
     if (!prefs)
diff --git a/gfx/thebes/src/gfxPlatformFontList.h b/gfx/thebes/src/gfxPlatformFontList.h
--- a/gfx/thebes/src/gfxPlatformFontList.h
+++ b/gfx/thebes/src/gfxPlatformFontList.h
@@ -103,16 +103,17 @@ public:
     virtual gfxFontEntry* GetDefaultFont(const gfxFontStyle* aStyle,
                                          PRBool& aNeedsBold) = 0;
 
     // look up a font by name on the host platform
     virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                           const nsAString& aFontName) = 0;
 
     // create a new platform font from downloaded data (@font-face)
+    // this method is responsible to ensure aFontData is NS_Free()'d
     virtual gfxFontEntry* MakePlatformFont(const gfxFontEntry *aProxyEntry,
                                            const PRUint8 *aFontData,
                                            PRUint32 aLength) = 0;
 
     // get the standard family name on the platform for a given font name
     virtual PRBool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) = 0;
 
 protected:
diff --git a/gfx/thebes/src/gfxPlatformGtk.cpp b/gfx/thebes/src/gfxPlatformGtk.cpp
--- a/gfx/thebes/src/gfxPlatformGtk.cpp
+++ b/gfx/thebes/src/gfxPlatformGtk.cpp
@@ -303,25 +303,20 @@ gfxFontEntry*
 gfxPlatformGtk::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFontName)
 {
     return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontName);
 }
 
 gfxFontEntry* 
 gfxPlatformGtk::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, 
-                                 nsISupports *aLoader,
                                  const PRUint8 *aFontData, PRUint32 aLength)
 {
-    // Just being consistent with other platforms.
-    // This will mean that only fonts in SFNT formats will be accepted.
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength))
-        return nsnull;
-
-    return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aLoader,
+    // passing ownership of the font data to the new font entry
+    return gfxPangoFontGroup::NewFontEntry(*aProxyEntry,
                                            aFontData, aLength);
 }
 
 PRBool
 gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags)
 {
     // check for strange format flags
     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
diff --git a/gfx/thebes/src/gfxPlatformMac.cpp b/gfx/thebes/src/gfxPlatformMac.cpp
--- a/gfx/thebes/src/gfxPlatformMac.cpp
+++ b/gfx/thebes/src/gfxPlatformMac.cpp
@@ -182,20 +182,26 @@ gfxPlatformMac::LookupLocalFont(const gf
                                 const nsAString& aFontName)
 {
     return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aProxyEntry, 
                                                                     aFontName);
 }
 
 gfxFontEntry* 
 gfxPlatformMac::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                 nsISupports *aLoader,
                                  const PRUint8 *aFontData, PRUint32 aLength)
 {
-    return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry, aFontData, aLength);
+    // Ownership of aFontData is passed in here.
+    // After activating the font via ATS, we can discard the data.
+    gfxFontEntry *fe =
+        gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry,
+                                                                  aFontData,
+                                                                  aLength);
+    NS_Free((void*)aFontData);
+    return fe;
 }
 
 PRBool
 gfxPlatformMac::IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags)
 {
     // check for strange format flags
     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
                  "strange font format hint set");
diff --git a/gfx/thebes/src/gfxUserFontSet.cpp b/gfx/thebes/src/gfxUserFontSet.cpp
--- a/gfx/thebes/src/gfxUserFontSet.cpp
+++ b/gfx/thebes/src/gfxUserFontSet.cpp
@@ -10,21 +10,22 @@
  * Software distributed under the License is distributed on an "AS IS" basis,
  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  * for the specific language governing rights and limitations under the
  * License.
  *
  * The Original Code is thebes gfx code.
  *
  * The Initial Developer of the Original Code is Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2008
+ * Portions created by the Initial Developer are Copyright (C) 2008-2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   John Daggett <jdaggett@mozilla.com>
+ *   Jonathan Kew <jfkthame@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -41,16 +42,18 @@
 #include "prlog.h"
 
 #include "gfxUserFontSet.h"
 #include "gfxPlatform.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "prlong.h"
 
+#include "woff.h"
+
 #ifdef PR_LOGGING
 static PRLogModuleInfo *gUserFontsLog = PR_NewLogModule("userfonts");
 #endif /* PR_LOGGING */
 
 #define LOG(args) PR_LOG(gUserFontsLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gUserFontsLog, PR_LOG_DEBUG)
 
 static PRUint64 sFontSetGeneration = LL_INIT(0, 0);
@@ -82,17 +85,16 @@ gfxProxyFontEntry::~gfxProxyFontEntry()
 gfxUserFontSet::gfxUserFontSet()
 {
     mFontFamilies.Init(5);
     IncrementGeneration();
 }
 
 gfxUserFontSet::~gfxUserFontSet()
 {
-
 }
 
 void
 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, 
                             const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 
                             PRUint32 aWeight, 
                             PRUint32 aStretch, 
                             PRUint32 aItalicStyle, 
@@ -164,70 +166,146 @@ gfxUserFontSet::FindFontEntry(const nsAS
     // search again
     if (status == STATUS_LOADED)
         return family->FindFontForStyle(aFontStyle, aNeedsBold);
 
     // if either loading or an error occurred, return null
     return nsnull;
 }
 
+// Given a buffer of downloaded font data, do any necessary preparation
+// to make it into usable OpenType.
+// May return the original pointer unchanged, or a newly-allocated
+// block (in which case the passed-in block is NS_Free'd).
+// aLength is updated if necessary to the new length of the data.
+// Returns NULL and NS_Free's the incoming data in case of errors.
+const PRUint8*
+PrepareOpenTypeData(const PRUint8* aData, PRUint32* aLength)
+{
+    switch(gfxFontUtils::DetermineFontDataType(aData, *aLength)) {
+    
+    case GFX_USERFONT_OPENTYPE:
+        // nothing to do
+        return aData;
+        
+    case GFX_USERFONT_WOFF: {
+        PRUint32 status = eWOFF_ok;
+        PRUint32 bufferSize = woffGetDecodedSize(aData, *aLength, &status);
+        if (WOFF_FAILURE(status)) {
+            break;
+        }
+        PRUint8* decodedData = static_cast<PRUint8*>(NS_Alloc(bufferSize));
+        if (!decodedData) {
+            break;
+        }
+        woffDecodeToBuffer(aData, *aLength,
+                           decodedData, bufferSize,
+                           aLength, &status);
+        // replace original data with the decoded version
+        NS_Free((void*)aData);
+        aData = decodedData;
+        if (WOFF_FAILURE(status)) {
+            // something went wrong, discard the data and return NULL
+            break;
+        }
+        // success, return the decoded data
+        return aData;
+    }
 
+    // xxx - add support for other wrappers here
+
+    default:
+        NS_WARNING("unknown font format");
+        break;
+    }
+
+    // discard downloaded data that couldn't be used
+    NS_Free((void*)aData);
+
+    return nsnull;
+}
+
+// This is called when a font download finishes.
+// Ownership of aFontData passes in here, and the font set must
+// ensure that it is eventually deleted via NS_Free().
 PRBool 
 gfxUserFontSet::OnLoadComplete(gfxFontEntry *aFontToLoad,
-                               nsISupports *aLoader,
                                const PRUint8 *aFontData, PRUint32 aLength, 
                                nsresult aDownloadStatus)
 {
     NS_ASSERTION(aFontToLoad->mIsProxy, "trying to load font data for wrong font entry type");
 
-    if (!aFontToLoad->mIsProxy)
+    if (!aFontToLoad->mIsProxy) {
+        NS_Free((void*)aFontData);
         return PR_FALSE;
+    }
 
     gfxProxyFontEntry *pe = static_cast<gfxProxyFontEntry*> (aFontToLoad);
 
     // download successful, make platform font using font data
     if (NS_SUCCEEDED(aDownloadStatus)) {
-        gfxFontEntry *fe = 
-            gfxPlatform::GetPlatform()->MakePlatformFont(pe, aLoader,
-                                                         aFontData, aLength);
+        gfxFontEntry *fe = nsnull;
+
+        // Unwrap/decompress or otherwise munge the downloaded data
+        // to make a usable sfnt structure.
+        // This may cause aFontData to point to a new buffer, or be NULL.
+        aFontData = PrepareOpenTypeData(aFontData, &aLength);
+        if (aFontData &&
+            gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
+            // Here ownership of aFontData is passed to the platform,
+            // which will delete it when no longer required
+            fe = gfxPlatform::GetPlatform()->MakePlatformFont(pe,
+                                                              aFontData,
+                                                              aLength);
+            aFontData = nsnull; // the platform may have freed the data now!
+        } else {
+            // the data was unusable, so just discard it
+            // (error will be reported below, if logging is enabled)
+            if (aFontData) {
+                NS_Free((void*)aFontData);
+            }
+        }
+
         if (fe) {
             static_cast<gfxMixedFontFamily*>(pe->mFamily)->ReplaceFontEntry(pe, fe);
             IncrementGeneration();
 #ifdef PR_LOGGING
             if (LOG_ENABLED()) {
                 nsCAutoString fontURI;
                 pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
-
-                LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n", 
-                     this, pe->mSrcIndex, fontURI.get(), 
-                     NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(), 
+                LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
+                     this, pe->mSrcIndex, fontURI.get(),
+                     NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(),
                      PRUint32(mGeneration)));
             }
 #endif
             return PR_TRUE;
         } else {
 #ifdef PR_LOGGING
             if (LOG_ENABLED()) {
                 nsCAutoString fontURI;
                 pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
-                LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error making platform font\n", 
-                     this, pe->mSrcIndex, fontURI.get(), 
+                LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error making platform font\n",
+                     this, pe->mSrcIndex, fontURI.get(),
                      NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get()));
             }
 #endif
         }
     } else {
         // download failed
+        if (aFontData) {
+            NS_Free((void*)aFontData);
+        }
 #ifdef PR_LOGGING
         if (LOG_ENABLED()) {
             nsCAutoString fontURI;
             pe->mSrcList[pe->mSrcIndex].mURI->GetSpec(fontURI);
-            LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error %8.8x downloading font data\n", 
-                 this, pe->mSrcIndex, fontURI.get(), 
-                 NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(), 
+            LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error %8.8x downloading font data\n",
+                 this, pe->mSrcIndex, fontURI.get(),
+                 NS_ConvertUTF16toUTF8(pe->mFamily->Name()).get(),
                  aDownloadStatus));
         }
 #endif
     }
 
     // error occurred, load next src
     LoadStatus status;
 
diff --git a/gfx/thebes/src/gfxWindowsFonts.cpp b/gfx/thebes/src/gfxWindowsFonts.cpp
--- a/gfx/thebes/src/gfxWindowsFonts.cpp
+++ b/gfx/thebes/src/gfxWindowsFonts.cpp
@@ -510,25 +510,24 @@ public:
         return eotReader->Read(outBuffer, aBytesToRead);
     }        
         
 };
 
 /* static */
 FontEntry* 
 FontEntry::LoadFont(const gfxProxyFontEntry &aProxyEntry, 
-                    nsISupports *aLoader,const PRUint8 *aFontData, 
-                    PRUint32 aLength) {
+                    const PRUint8 *aFontData, 
+                    PRUint32 aLength)
+{
     // if calls aren't available, bail
     if (!TTLoadEmbeddedFontPtr || !TTDeleteEmbeddedFontPtr)
         return nsnull;
 
-    PRBool isCFF;
-    if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength, &isCFF))
-        return nsnull;
+    PRBool isCFF = gfxFontUtils::IsCffFont(aFontData);
         
     nsresult rv;
     HANDLE fontRef = nsnull;
     PRBool isEmbedded = PR_FALSE;
 
     nsAutoString uniqueName;
     rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
     if (NS_FAILED(rv))
diff --git a/gfx/thebes/src/gfxWindowsPlatform.cpp b/gfx/thebes/src/gfxWindowsPlatform.cpp
--- a/gfx/thebes/src/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/src/gfxWindowsPlatform.cpp
@@ -836,23 +836,28 @@ gfxWindowsPlatform::LookupLocalFont(cons
     return data.mFontEntry;
 #else
     return FontEntry::LoadLocalFont(*aProxyEntry, aFontName);
 #endif
 }
 
 gfxFontEntry* 
 gfxWindowsPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
-                                     nsISupports *aLoader,
                                      const PRUint8 *aFontData, PRUint32 aLength)
 {
 #ifdef MOZ_FT2_FONTS
-    return FontEntry::CreateFontEntry(*aProxyEntry, aLoader, aFontData, aLength);
+    // The FT2 font needs the font data to persist, so we do NOT free it here
+    // but instead pass ownership to the font entry.
+    // Deallocation will happen later, when the font face is destroyed.
+    return FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength);
 #else
-    return FontEntry::LoadFont(*aProxyEntry, aLoader, aFontData, aLength);
+    // With GDI, we can free the downloaded data after activating the font
+    gfxFontEntry *fe = FontEntry::LoadFont(*aProxyEntry, aFontData, aLength);
+    NS_Free((void*)aFontData);
+    return fe;
 #endif    
 }
 
 PRBool
 gfxWindowsPlatform::IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags)
 {
     // check for strange format flags
     NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
diff --git a/gfx/thebes/src/woff-private.h b/gfx/thebes/src/woff-private.h
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/src/woff-private.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is WOFF font packaging code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef WOFF_PRIVATE_H_
+#define WOFF_PRIVATE_H_
+
+#include "woff.h"
+
+/* private definitions used in the WOFF encoder/decoder functions */
+
+/* create an OT tag from 4 characters */
+#define TAG(a,b,c,d) ((a)<<24 | (b)<<16 | (c)<<8 | (d))
+
+#define WOFF_SIGNATURE    TAG('w','O','F','F')
+
+#define SFNT_VERSION_CFF  TAG('O','T','T','O')
+#define SFNT_VERSION_TT   0x00010000
+#define SFNT_VERSION_true TAG('t','r','u','e')
+
+#define TABLE_TAG_DSIG    TAG('D','S','I','G')
+#define TABLE_TAG_head    TAG('h','e','a','d')
+#define TABLE_TAG_bhed    TAG('b','h','e','d')
+
+#define SFNT_CHECKSUM_CALC_CONST  0xB1B0AFBAU /* from the TT/OT spec */
+
+#ifdef WOFF_MOZILLA_CLIENT
+# include <prnetdb.h> 
+# define READ32BE(x) PR_ntohl(x)
+# define READ16BE(x) PR_ntohs(x)
+#else
+/* These macros to read values as big-endian only work on "real" variables,
+   not general expressions, because of the use of &(x), but they are
+   designed to work on both BE and LE machines without the need for a
+   configure check. For production code, we might want to replace this
+   with something more efficient. */
+/* read a 32-bit BigEndian value */
+# define READ32BE(x) ( ( (uint32_t) ((uint8_t*)&(x))[0] << 24 ) + \
+                       ( (uint32_t) ((uint8_t*)&(x))[1] << 16 ) + \
+                       ( (uint32_t) ((uint8_t*)&(x))[2] <<  8 ) + \
+                         (uint32_t) ((uint8_t*)&(x))[3]           )
+/* read a 16-bit BigEndian value */
+# define READ16BE(x) ( ( (uint16_t) ((uint8_t*)&(x))[0] << 8 ) + \
+                         (uint16_t) ((uint8_t*)&(x))[1]          )
+#endif
+
+#pragma pack(push,1)
+
+typedef struct {
+  uint32_t version;
+  uint16_t numTables;
+  uint16_t searchRange;
+  uint16_t entrySelector;
+  uint16_t rangeShift;
+} sfntHeader;
+
+typedef struct {
+  uint32_t tag;
+  uint32_t checksum;
+  uint32_t offset;
+  uint32_t length;
+} sfntDirEntry;
+
+typedef struct {
+  uint32_t signature;
+  uint32_t flavor;
+  uint32_t length;
+  uint16_t numTables;
+  uint16_t reserved;
+  uint32_t totalSfntSize;
+  uint16_t majorVersion;
+  uint16_t minorVersion;
+  uint32_t metaOffset;
+  uint32_t metaCompLen;
+  uint32_t metaOrigLen;
+  uint32_t privOffset;
+  uint32_t privLen;
+} woffHeader;
+
+typedef struct {
+  uint32_t tag;
+  uint32_t offset;
+  uint32_t compLen;
+  uint32_t origLen;
+  uint32_t checksum;
+} woffDirEntry;
+
+typedef struct {
+  uint32_t version;
+  uint32_t fontRevision;
+  uint32_t checkSumAdjustment;
+  uint32_t magicNumber;
+  uint16_t flags;
+  uint16_t unitsPerEm;
+  uint32_t created[2];
+  uint32_t modified[2];
+  int16_t xMin;
+  int16_t yMin;
+  int16_t xMax;
+  int16_t yMax;
+  uint16_t macStyle;
+  uint16_t lowestRecPpem;
+  int16_t fontDirectionHint;
+  int16_t indexToLocFormat;
+  int16_t glyphDataFormat;
+} sfntHeadTable;
+
+#define HEAD_TABLE_SIZE 54 /* sizeof(sfntHeadTable) may report 56 because of alignment */
+
+typedef struct {
+  uint32_t offset;
+  uint16_t oldIndex;
+  uint16_t newIndex;
+} tableOrderRec;
+
+#pragma pack(pop)
+
+#endif
diff --git a/gfx/thebes/src/woff.c b/gfx/thebes/src/woff.c
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/src/woff.c
@@ -0,0 +1,1170 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is WOFF font packaging code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "woff-private.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#ifdef WOFF_MOZILLA_CLIENT /* define this when building as part of Gecko */
+# include "prmem.h"
+# define malloc  PR_Malloc
+# define realloc PR_Realloc
+# define free    PR_Free
+#endif
+
+/*
+ * Just simple whole-file encoding and decoding functions; a more extensive
+ * WOFF library could provide support for accessing individual tables from a
+ * compressed font, alternative options for memory allocation/ownership and
+ * error handling, etc.
+ */
+
+/* on errors, each function sets a status variable and jumps to failure: */
+#undef FAIL
+#define FAIL(err) do { status |= err; goto failure; } while (0)
+
+/* adjust an offset for longword alignment */
+#define LONGALIGN(x) (((x) + 3) & ~3)
+
+static int
+compareOffsets(const void * lhs, const void * rhs)
+{
+  const tableOrderRec * a = (const tableOrderRec *) lhs;
+  const tableOrderRec * b = (const tableOrderRec *) rhs;
+  /* don't simply return a->offset - b->offset because these are unsigned
+     offset values; could convert to int, but possible integer overflow */
+  return a->offset > b->offset ? 1 :
+         a->offset < b->offset ? -1 :
+         0;
+}
+
+#ifndef WOFF_MOZILLA_CLIENT
+
+/******************************************************************/
+/* * * * * * * * * * * * * * ENCODING * * * * * * * * * * * * * * */
+/******************************************************************/
+
+static uint32_t
+calcChecksum(const sfntDirEntry * dirEntry,
+             const uint8_t * sfntData, uint32_t sfntLen)
+{
+  /* just returns zero on errors, they will be detected again elsewhere */
+  const uint32_t * csumPtr;
+  const uint32_t * csumEnd;
+  uint32_t csum = 0;
+  uint32_t length = LONGALIGN(READ32BE(dirEntry->length));
+  uint32_t offset = READ32BE(dirEntry->offset);
+  uint32_t tag;
+  if ((offset & 3) != 0) {
+    return csum;
+  }
+  if (length > sfntLen || offset > sfntLen - length) {
+    return csum;
+  }
+  csumPtr = (const uint32_t *) (sfntData + offset);
+  csumEnd = csumPtr + length / 4;
+  while (csumPtr < csumEnd) {
+    csum += READ32BE(*csumPtr);
+    csumPtr++;
+  }
+  tag = READ32BE(dirEntry->tag);
+  if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
+    const sfntHeadTable * head;
+    if (length < HEAD_TABLE_SIZE) {
+      return 0;
+    }
+    head = (const sfntHeadTable *)(sfntData + offset);
+    csum -= READ32BE(head->checkSumAdjustment);
+  }
+  return csum;
+}
+
+const uint8_t *
+woffEncode(const uint8_t * sfntData, uint32_t sfntLen,
+           uint16_t majorVersion, uint16_t minorVersion,
+           uint32_t * woffLen, uint32_t * pStatus)
+{
+  uint8_t * woffData = NULL;
+  tableOrderRec * tableOrder = NULL;
+
+  uint32_t tableOffset;
+  uint32_t totalSfntSize;
+
+  uint16_t numOrigTables;
+  uint16_t numTables;
+  uint16_t tableIndex;
+  uint16_t order;
+  const sfntDirEntry * sfntDir;
+  uint32_t tableBase;
+  uint32_t checkSumAdjustment = 0;
+  woffHeader * newHeader;
+  uint32_t tag = 0;
+  uint32_t removedDsigSize = 0;
+  uint32_t status = eWOFF_ok;
+
+  const sfntHeader * header = (const sfntHeader *) (sfntData);
+  const sfntHeadTable * head = NULL;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return NULL;
+  }
+
+  if (READ32BE(header->version) != SFNT_VERSION_TT &&
+      READ32BE(header->version) != SFNT_VERSION_CFF &&
+      READ32BE(header->version) != SFNT_VERSION_true) {
+    status |= eWOFF_warn_unknown_version;
+  }
+
+  numOrigTables = READ16BE(header->numTables);
+  sfntDir = (const sfntDirEntry *) (sfntData + sizeof(sfntHeader));
+
+  for (tableIndex = 0; tableIndex < numOrigTables; ++tableIndex) {
+    /* validate table checksums, to figure out if we need to drop DSIG;
+       also check that table directory is correctly sorted */
+    uint32_t prevTag = tag;
+    uint32_t csum = calcChecksum(&sfntDir[tableIndex], sfntData, sfntLen);
+    if (csum != READ32BE(sfntDir[tableIndex].checksum)) {
+      status |= eWOFF_warn_checksum_mismatch;
+    }
+    checkSumAdjustment += csum;
+    tag = READ32BE(sfntDir[tableIndex].tag);
+    if (tag <= prevTag) {
+      FAIL(eWOFF_invalid);
+    }
+    if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
+      if (READ32BE(sfntDir[tableIndex].length) < HEAD_TABLE_SIZE) {
+        FAIL(eWOFF_invalid);
+      }
+      head = (const sfntHeadTable *)(sfntData +
+                                     READ32BE(sfntDir[tableIndex].offset));
+    }
+  }
+  if (!head) {
+    FAIL(eWOFF_invalid);
+  }
+  if ((status & eWOFF_warn_checksum_mismatch) == 0) {
+    /* no point even checking if we already have an error,
+       as fixing that will change the overall checksum too */
+    const uint32_t * csumPtr = (const uint32_t *) sfntData;
+    const uint32_t * csumEnd = csumPtr + 3 + 4 * numOrigTables;
+    while (csumPtr < csumEnd) {
+      checkSumAdjustment += READ32BE(*csumPtr);
+      ++csumPtr;
+    }
+    checkSumAdjustment = 0xB1B0AFBA - checkSumAdjustment;
+    if (checkSumAdjustment != READ32BE(head->checkSumAdjustment)) {
+      status |= eWOFF_warn_checksum_mismatch;
+    }
+  }
+
+  /* Fixing checkSumAdjustment is tricky, because if there's a DSIG table,
+     we're going to have to remove that, which in turn means that table
+     offsets in the directory will all change.
+     And recalculating checkSumAdjustment requires taking account of any
+     individual table checksum corrections, but they have not actually been
+     applied to the sfnt data at this point.
+     And finally, we'd need to get the corrected checkSumAdjustment into the
+     encoded head table (but we can't modify the original sfnt data).
+     An easier way out seems to be to go ahead and encode the font, knowing
+     that checkSumAdjustment will be wrong; then (if the status flag
+     eWOFF_warn_checksum_mismatch is set) we'll decode the font back to
+     sfnt format. This will fix up the checkSumAdjustment (and return a
+     warning status). We'll ignore that warning, and then re-encode the
+     new, cleaned-up sfnt to get the final WOFF data. Perhaps not the most
+     efficient approach, but it seems simpler than trying to predict the
+     correct final checkSumAdjustment and incorporate it into the head
+     table on the fly. */
+
+  tableOrder = (tableOrderRec *) malloc(numOrigTables * sizeof(tableOrderRec));
+  if (!tableOrder) {
+    FAIL(eWOFF_out_of_memory);
+  }
+  for (tableIndex = 0, numTables = 0;
+       tableIndex < numOrigTables; ++tableIndex) {
+    if ((status & eWOFF_warn_checksum_mismatch) != 0) {
+      /* check for DSIG table that we must drop if we're fixing checksums */
+      tag = READ32BE(sfntDir[tableIndex].tag);
+      if (tag == TABLE_TAG_DSIG) {
+        status |= eWOFF_warn_removed_DSIG;
+        removedDsigSize = READ32BE(sfntDir[tableIndex].length);
+        continue;
+      }
+    }
+    tableOrder[numTables].offset = READ32BE(sfntDir[tableIndex].offset);
+    tableOrder[numTables].oldIndex = tableIndex;
+    tableOrder[numTables].newIndex = numTables;
+    ++numTables;
+  }
+  qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets);
+
+  /* initially, allocate space for header and directory */
+  tableOffset = sizeof(woffHeader) + numTables * sizeof(woffDirEntry);
+  woffData = (uint8_t *) malloc(tableOffset);
+  if (!woffData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  /* accumulator for total expected size of decoded font */
+  totalSfntSize = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry);
+
+/*
+ * We use a macro for this rather than creating a variable because woffData
+ * will get reallocated during encoding. The macro avoids the risk of using a
+ * stale pointer, and the compiler should optimize multiple successive uses.
+ */
+#define WOFFDIR ((woffDirEntry *) (woffData + sizeof(woffHeader)))
+
+  for (order = 0; order < numTables; ++order) {
+    uLong sourceLen, destLen;
+    uint32_t sourceOffset;
+
+    uint16_t oldIndex = tableOrder[order].oldIndex;
+    uint16_t newIndex = tableOrder[order].newIndex;
+
+    WOFFDIR[newIndex].tag = sfntDir[oldIndex].tag;
+    if ((status & eWOFF_warn_checksum_mismatch) != 0) {
+      uint32_t csum = calcChecksum(&sfntDir[oldIndex], sfntData, sfntLen);
+      WOFFDIR[newIndex].checksum = READ32BE(csum);
+    } else {
+      WOFFDIR[newIndex].checksum = sfntDir[oldIndex].checksum;
+    }
+    WOFFDIR[newIndex].origLen = sfntDir[oldIndex].length;
+
+    /* we always realloc woffData to a long-aligned size, so this is safe */
+    while ((tableOffset & 3) != 0) {
+      woffData[tableOffset++] = 0;
+    }
+    WOFFDIR[newIndex].offset = READ32BE(tableOffset);
+
+    /* allocate enough space for upper bound of compressed size */
+    sourceOffset = READ32BE(sfntDir[oldIndex].offset);
+    if ((sourceOffset & 3) != 0) {
+      status |= eWOFF_warn_misaligned_table;
+    }
+    sourceLen = READ32BE(sfntDir[oldIndex].length);
+    if (sourceLen > sfntLen || sourceOffset > sfntLen - sourceLen) {
+      FAIL(eWOFF_invalid);
+    }
+    destLen = LONGALIGN(compressBound(sourceLen));
+    woffData = (uint8_t *) realloc(woffData, tableOffset + destLen);
+    if (!woffData) {
+      FAIL(eWOFF_out_of_memory);
+    }
+
+    /* do the compression directly into the WOFF data block */
+    if (compress2((Bytef *) (woffData + tableOffset), &destLen,
+                  (const Bytef *) (sfntData + sourceOffset),
+                  sourceLen, 9) != Z_OK) {
+      FAIL(eWOFF_compression_failure);
+    }
+    if (destLen < sourceLen) {
+      /* compressed table was smaller */
+      tableOffset += destLen;
+      WOFFDIR[newIndex].compLen = READ32BE(destLen);
+    } else {
+      /* compression didn't make it smaller, so store original data instead */
+      destLen = sourceLen;
+      /* reallocate to ensure enough space for the table,
+         plus potential padding after it */
+      woffData = (uint8_t *) realloc(woffData,
+                                     tableOffset + LONGALIGN(sourceLen));
+      if (!woffData) {
+        FAIL(eWOFF_out_of_memory);
+      }
+      /* copy the original data into place */
+      memcpy(woffData + tableOffset,
+             sfntData + READ32BE(sfntDir[oldIndex].offset), sourceLen);
+      tableOffset += sourceLen;
+      WOFFDIR[newIndex].compLen = WOFFDIR[newIndex].origLen;
+    }
+
+    /* update total size of uncompressed OpenType with table size */
+    totalSfntSize += sourceLen;
+    totalSfntSize = LONGALIGN(totalSfntSize);
+  }
+
+  if (totalSfntSize > sfntLen) {
+    if (totalSfntSize > LONGALIGN(sfntLen)) {
+      FAIL(eWOFF_invalid);
+    } else {
+      status |= eWOFF_warn_unpadded_table;
+    }
+  } else if (totalSfntSize < sfntLen) {
+    /* check if the remaining data is a DSIG we're removing;
+       if so, we're already warning about that */
+    if ((status & eWOFF_warn_removed_DSIG) != 0 ||
+        sfntLen - totalSfntSize >
+          LONGALIGN(removedDsigSize) + sizeof(sfntDirEntry)) {
+      status |= eWOFF_warn_trailing_data;
+    }
+  }
+
+  /* write the header */
+  newHeader = (woffHeader *) (woffData);
+  newHeader->signature = WOFF_SIGNATURE;
+  newHeader->signature = READ32BE(newHeader->signature);
+  newHeader->flavor = header->version;
+  newHeader->length = READ32BE(tableOffset);
+  newHeader->numTables = READ16BE(numTables);
+  newHeader->reserved = 0;
+  newHeader->totalSfntSize = READ32BE(totalSfntSize);
+  newHeader->majorVersion = READ16BE(majorVersion);
+  newHeader->minorVersion = READ16BE(minorVersion);
+  newHeader->metaOffset = 0;
+  newHeader->metaCompLen = 0;
+  newHeader->metaOrigLen = 0;
+  newHeader->privOffset = 0;
+  newHeader->privLen = 0;
+
+  free(tableOrder);
+
+  if ((status & eWOFF_warn_checksum_mismatch) != 0) {
+    /* The original font had checksum errors, so we now decode our WOFF data
+       back to sfnt format (which fixes checkSumAdjustment), then re-encode
+       to get a clean copy. */
+    const uint8_t * cleanSfnt = woffDecode(woffData, tableOffset,
+                                           &sfntLen, &status);
+    if (WOFF_FAILURE(status)) {
+      FAIL(status);
+    }
+    free(woffData);
+    woffData = (uint8_t *) woffEncode(cleanSfnt, sfntLen,
+                                      majorVersion, minorVersion,
+                                      &tableOffset, &status);
+    free((void *) cleanSfnt);
+    if (WOFF_FAILURE(status)) {
+      FAIL(status);
+    }
+  }
+
+  if (woffLen) {
+    *woffLen = tableOffset;
+  }
+  if (pStatus) {
+    *pStatus |= status;
+  }
+  return woffData;
+
+failure:
+  if (tableOrder) {
+    free(tableOrder);
+  }
+  if (woffData) {
+    free(woffData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+static const uint8_t *
+rebuildWoff(const uint8_t * woffData, uint32_t * woffLen,
+            const uint8_t * metaData, uint32_t metaCompLen, uint32_t metaOrigLen,
+            const uint8_t * privData, uint32_t privLen, uint32_t * pStatus)
+{
+  const woffHeader * origHeader;
+  const woffDirEntry * woffDir;
+  uint8_t * newData = NULL;
+  uint8_t * tableData = NULL;
+  woffHeader * newHeader;
+  uint16_t numTables;
+  uint32_t tableLimit, totalSize, offset;
+  uint16_t i;
+  uint32_t status = eWOFF_ok;
+
+  if (*woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  origHeader = (const woffHeader *) (woffData);
+
+  if (READ32BE(origHeader->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  numTables = READ16BE(origHeader->numTables);
+  woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+  tableLimit = 0;
+  for (i = 0; i < numTables; ++i) {
+    uint32_t end = READ32BE(woffDir[i].offset) + READ32BE(woffDir[i].compLen);
+    if (end > tableLimit) {
+      tableLimit = end;
+    }
+  }
+  tableLimit = LONGALIGN(tableLimit);
+
+  /* check for broken input (meta/priv data before sfnt tables) */
+  offset = READ32BE(origHeader->metaOffset);
+  if (offset != 0 && offset < tableLimit) {
+    FAIL(eWOFF_illegal_order);
+  }
+  offset = READ32BE(origHeader->privOffset);
+  if (offset != 0 && offset < tableLimit) {
+    FAIL(eWOFF_illegal_order);
+  }
+
+  totalSize = tableLimit; /* already long-aligned */
+  if (metaCompLen) {
+    totalSize += metaCompLen;
+  }
+  if (privLen) {
+    totalSize = LONGALIGN(totalSize) + privLen;
+  }
+  newData = malloc(totalSize);
+  if (!newData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  /* copy the header, directory, and sfnt tables */
+  memcpy(newData, woffData, tableLimit);
+
+  /* then overwrite the header fields that should be changed */
+  newHeader = (woffHeader *) newData;
+  newHeader->length = READ32BE(totalSize);
+  newHeader->metaOffset = 0;
+  newHeader->metaCompLen = 0;
+  newHeader->metaOrigLen = 0;
+  newHeader->privOffset = 0;
+  newHeader->privLen = 0;
+
+  offset = tableLimit;
+  if (metaData && metaCompLen > 0 && metaOrigLen > 0) {
+    newHeader->metaOffset = READ32BE(offset);
+    newHeader->metaCompLen = READ32BE(metaCompLen);
+    newHeader->metaOrigLen = READ32BE(metaOrigLen);
+    memcpy(newData + offset, metaData, metaCompLen);
+    offset += metaCompLen;
+  }
+
+  if (privData && privLen > 0) {
+    while ((offset & 3) != 0) {
+      newData[offset++] = 0;
+    }
+    newHeader->privOffset = READ32BE(offset);
+    newHeader->privLen = READ32BE(privLen);
+    memcpy(newData + offset, privData, privLen);
+    offset += privLen;
+  }
+
+  *woffLen = offset;
+  free((void *) woffData);
+
+  if (pStatus) {
+    *pStatus |= status;
+  }
+  return newData;
+
+failure:
+  if (newData) {
+    free(newData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+const uint8_t *
+woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen,
+                const uint8_t * metaData, uint32_t metaLen,
+                uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uLong compLen = 0;
+  uint8_t * compData = NULL;
+  const uint8_t * privData = NULL;
+  uint32_t privLen = 0;
+  uint32_t status = eWOFF_ok;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return NULL;
+  }
+
+  if (!woffData || !woffLen) {
+    FAIL(eWOFF_bad_parameter);
+  }
+
+  if (*woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  header = (const woffHeader *) (woffData);
+
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  if (header->privOffset != 0 && header->privLen != 0) {
+    privData = woffData + READ32BE(header->privOffset);
+    privLen = READ32BE(header->privLen);
+    if (privData + privLen > woffData + *woffLen) {
+      FAIL(eWOFF_invalid);
+    }
+  }
+
+  if (metaData && metaLen > 0) {
+    compLen = compressBound(metaLen);
+    compData = malloc(compLen);
+    if (!compData) {
+      FAIL(eWOFF_out_of_memory);
+    }
+
+    if (compress2((Bytef *) compData, &compLen,
+                  (const Bytef *) metaData, metaLen, 9) != Z_OK) {
+      FAIL(eWOFF_compression_failure);
+    }
+  }
+
+  woffData = rebuildWoff(woffData, woffLen,
+                         compData, compLen, metaLen,
+                         privData, privLen, pStatus);
+  free(compData);
+  return woffData;
+
+failure:
+  if (compData) {
+    free(compData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+const uint8_t *
+woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen,
+                   const uint8_t * privData, uint32_t privLen,
+                   uint32_t * pStatus)
+{
+  const woffHeader * header;
+  const uint8_t * metaData = NULL;
+  uint32_t metaLen = 0;
+  uint32_t status = eWOFF_ok;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return NULL;
+  }
+
+  if (!woffData || !woffLen) {
+    FAIL(eWOFF_bad_parameter);
+  }
+
+  if (*woffLen < sizeof(woffHeader)) {
+    FAIL(eWOFF_invalid);
+  }
+  header = (const woffHeader *) (woffData);
+
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    FAIL(eWOFF_bad_signature);
+  }
+
+  if (header->metaOffset != 0 && header->metaCompLen != 0) {
+    metaData = woffData + READ32BE(header->metaOffset);
+    metaLen = READ32BE(header->metaCompLen);
+    if (metaData + metaLen > woffData + *woffLen) {
+      FAIL(eWOFF_invalid);
+    }
+  }
+
+  woffData = rebuildWoff(woffData, woffLen,
+                         metaData, metaLen, READ32BE(header->metaOrigLen),
+                         privData, privLen, pStatus);
+  return woffData;
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+#endif /* WOFF_MOZILLA_CLIENT */
+
+/******************************************************************/
+/* * * * * * * * * * * * * * DECODING * * * * * * * * * * * * * * */
+/******************************************************************/
+
+static uint32_t
+sanityCheck(const uint8_t * woffData, uint32_t woffLen)
+{
+  const woffHeader * header;
+  uint16_t numTables, i;
+  const woffDirEntry * dirEntry;
+  uint32_t tableTotal = 0;
+
+  if (!woffData || !woffLen) {
+    return eWOFF_bad_parameter;
+  }
+
+  if (woffLen < sizeof(woffHeader)) {
+    return eWOFF_invalid;
+  }
+
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->signature) != WOFF_SIGNATURE) {
+    return eWOFF_bad_signature;
+  }
+
+  if (READ32BE(header->length) != woffLen || header->reserved != 0) {
+    return eWOFF_invalid;
+  }
+
+  numTables = READ16BE(header->numTables);
+  if (woffLen < sizeof(woffHeader) + numTables * sizeof(woffDirEntry)) {
+    return eWOFF_invalid;
+  }
+
+  dirEntry = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+  for (i = 0; i < numTables; ++i) {
+    uint32_t offs = READ32BE(dirEntry->offset);
+    uint32_t orig = READ32BE(dirEntry->origLen);
+    uint32_t comp = READ32BE(dirEntry->compLen);
+    if (comp > orig || comp > woffLen || offs > woffLen - comp) {
+      return eWOFF_invalid;
+    }
+    orig = (orig + 3) & ~3;
+    if (tableTotal > 0xffffffffU - orig) {
+      return eWOFF_invalid;
+    }
+    tableTotal += orig;
+    ++dirEntry;
+  }
+
+  if (tableTotal > 0xffffffffU - sizeof(sfntHeader) -
+                                 numTables * sizeof(sfntDirEntry) ||
+      READ32BE(header->totalSfntSize) !=
+        tableTotal + sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry)) {
+    return eWOFF_invalid;
+  }
+
+  return eWOFF_ok;
+}
+
+uint32_t
+woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen,
+                   uint32_t * pStatus)
+{
+  uint32_t status = eWOFF_ok;
+  uint32_t totalLen = 0;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return 0;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  totalLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize);
+  /* totalLen must be correctly rounded up to 4-byte alignment, otherwise
+     sanityCheck would have failed */
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return totalLen;
+}
+
+static void
+woffDecodeToBufferInternal(const uint8_t * woffData, uint32_t woffLen,
+                           uint8_t * sfntData, uint32_t bufferLen,
+                           uint32_t * pActualSfntLen, uint32_t * pStatus)
+{
+  /* this is only called after sanityCheck has verified that
+     (a) basic header fields are ok
+     (b) all the WOFF table offset/length pairs are valid (within the data)
+     (c) the sum of original sizes + header/directory matches totalSfntSize
+     so we don't have to re-check those overflow conditions here */
+  tableOrderRec * tableOrder = NULL;
+  const woffHeader * header;
+  uint16_t numTables;
+  uint16_t tableIndex;
+  uint16_t order;
+  const woffDirEntry * woffDir;
+  uint32_t totalLen;
+  sfntHeader * newHeader;
+  uint16_t searchRange, rangeShift, entrySelector;
+  uint32_t offset;
+  sfntDirEntry * sfntDir;
+  uint32_t headOffset = 0, headLength = 0;
+  sfntHeadTable * head;
+  uint32_t csum = 0;
+  const uint32_t * csumPtr;
+  uint32_t oldCheckSumAdjustment;
+  uint32_t status = eWOFF_ok;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return;
+  }
+
+  /* check basic header fields */
+  header = (const woffHeader *) (woffData);
+  if (READ32BE(header->flavor) != SFNT_VERSION_TT &&
+      READ32BE(header->flavor) != SFNT_VERSION_CFF &&
+      READ32BE(header->flavor) != SFNT_VERSION_true) {
+    status |= eWOFF_warn_unknown_version;
+  }
+
+  numTables = READ16BE(header->numTables);
+  woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+
+  totalLen = READ32BE(header->totalSfntSize);
+
+  /* construct the sfnt header */
+  newHeader = (sfntHeader *) (sfntData);
+  newHeader->version = header->flavor;
+  newHeader->numTables = READ16BE(numTables);
+  
+  /* calculate header fields for binary search */
+  searchRange = numTables;
+  searchRange |= (searchRange >> 1);
+  searchRange |= (searchRange >> 2);
+  searchRange |= (searchRange >> 4);
+  searchRange |= (searchRange >> 8);
+  searchRange &= ~(searchRange >> 1);
+  searchRange *= 16;
+  newHeader->searchRange = READ16BE(searchRange);
+  rangeShift = numTables * 16 - searchRange;
+  newHeader->rangeShift = READ16BE(rangeShift);
+  entrySelector = 0;
+  while (searchRange > 16) {
+    ++entrySelector;
+    searchRange >>= 1;
+  }
+  newHeader->entrySelector = READ16BE(entrySelector);
+
+  tableOrder = (tableOrderRec *) malloc(numTables * sizeof(tableOrderRec));
+  if (!tableOrder) {
+    FAIL(eWOFF_out_of_memory);
+  }
+  for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+    tableOrder[tableIndex].offset = READ32BE(woffDir[tableIndex].offset);
+    tableOrder[tableIndex].oldIndex = tableIndex;
+  }
+  qsort(tableOrder, numTables, sizeof(tableOrderRec), compareOffsets);
+
+  /* process each table, filling in the sfnt directory */
+  offset = sizeof(sfntHeader) + numTables * sizeof(sfntDirEntry);
+  sfntDir = (sfntDirEntry *) (sfntData + sizeof(sfntHeader));
+  for (order = 0; order < numTables; ++order) {
+    uint32_t origLen, compLen, tag, sourceOffset;
+    tableIndex = tableOrder[order].oldIndex;
+
+    /* validity of these was confirmed by sanityCheck */
+    origLen = READ32BE(woffDir[tableIndex].origLen);
+    compLen = READ32BE(woffDir[tableIndex].compLen);
+    sourceOffset = READ32BE(woffDir[tableIndex].offset);
+
+    sfntDir[tableIndex].tag = woffDir[tableIndex].tag;
+    sfntDir[tableIndex].offset = READ32BE(offset);
+    sfntDir[tableIndex].length = woffDir[tableIndex].origLen;
+    sfntDir[tableIndex].checksum = woffDir[tableIndex].checksum;
+    csum += READ32BE(sfntDir[tableIndex].checksum);
+
+    if (compLen < origLen) {
+      uLongf destLen = origLen;
+      if (uncompress((Bytef *)(sfntData + offset), &destLen,
+                     (const Bytef *)(woffData + sourceOffset),
+                     compLen) != Z_OK || destLen != origLen) {
+        FAIL(eWOFF_compression_failure);
+      }
+    } else {
+      memcpy(sfntData + offset, woffData + sourceOffset, origLen);
+    }
+
+    /* note that old Mac bitmap-only fonts have no 'head' table
+       (eg NISC18030.ttf) but a 'bhed' table instead */
+    tag = READ32BE(sfntDir[tableIndex].tag);
+    if (tag == TABLE_TAG_head || tag == TABLE_TAG_bhed) {
+      headOffset = offset;
+      headLength = origLen;
+    }
+
+    offset += origLen;
+
+    while (offset < totalLen && (offset & 3) != 0) {
+      sfntData[offset++] = 0;
+    }
+  }
+
+  if (headOffset > 0) {
+    /* the font checksum in the 'head' table depends on all the individual
+       table checksums (collected above), plus the header and directory
+       which are added in here */
+    if (headLength < HEAD_TABLE_SIZE) {
+      FAIL(eWOFF_invalid);
+    }
+    head = (sfntHeadTable *)(sfntData + headOffset);
+    oldCheckSumAdjustment = READ32BE(head->checkSumAdjustment);
+    head->checkSumAdjustment = 0;
+    csumPtr = (const uint32_t *)sfntData;
+    while (csumPtr < (const uint32_t *)(sfntData + sizeof(sfntHeader) +
+                                        numTables * sizeof(sfntDirEntry))) {
+      csum += READ32BE(*csumPtr);
+      csumPtr++;
+    }
+    csum = SFNT_CHECKSUM_CALC_CONST - csum;
+
+    if (oldCheckSumAdjustment != csum) {
+      /* if the checksum doesn't match, we fix it; but this will invalidate
+         any DSIG that may be present */
+      status |= eWOFF_warn_checksum_mismatch;
+    }
+    head->checkSumAdjustment = READ32BE(csum);
+  }
+
+  if (pActualSfntLen) {
+    *pActualSfntLen = totalLen;
+  }
+  if (pStatus) {
+    *pStatus |= status;
+  }
+  free(tableOrder);
+  return;
+
+failure:
+  if (tableOrder) {
+    free(tableOrder);
+  }
+  if (pActualSfntLen) {
+    *pActualSfntLen = 0;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+}
+
+void
+woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen,
+                   uint8_t * sfntData, uint32_t bufferLen,
+                   uint32_t * pActualSfntLen, uint32_t * pStatus)
+{
+  uint32_t status = eWOFF_ok;
+  uint32_t totalLen;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  if (!sfntData) {
+    FAIL(eWOFF_bad_parameter);
+  }
+
+  totalLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize);
+  if (bufferLen < totalLen) {
+    FAIL(eWOFF_buffer_too_small);
+  }
+
+  woffDecodeToBufferInternal(woffData, woffLen, sfntData, bufferLen,
+                             pActualSfntLen, pStatus);
+  return;
+
+failure:
+  if (pActualSfntLen) {
+    *pActualSfntLen = 0;
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+}
+
+const uint8_t *
+woffDecode(const uint8_t * woffData, uint32_t woffLen,
+           uint32_t * sfntLen, uint32_t * pStatus)
+{
+  uint32_t status = eWOFF_ok;
+  uint8_t * sfntData = NULL;
+  uint32_t bufLen;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return NULL;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  bufLen = READ32BE(((const woffHeader *) (woffData))->totalSfntSize);
+  sfntData = (uint8_t *) malloc(bufLen);
+  if (!sfntData) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  woffDecodeToBufferInternal(woffData, woffLen, sfntData, bufLen,
+                             sfntLen, &status);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  if (pStatus) {
+    *pStatus |= status;
+  }
+  return sfntData;
+
+failure:
+  if (sfntData) {
+    free(sfntData);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;
+}
+
+#ifndef WOFF_MOZILLA_CLIENT
+
+const uint8_t *
+woffGetMetadata(const uint8_t * woffData, uint32_t woffLen,
+                uint32_t * metaLen, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint32_t offset, compLen;
+  uLong origLen;
+  uint8_t * data = NULL;
+  uint32_t status = eWOFF_ok;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return NULL;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  header = (const woffHeader *) (woffData);
+
+  offset = READ32BE(header->metaOffset);
+  compLen = READ32BE(header->metaCompLen);
+  origLen = READ32BE(header->metaOrigLen);
+  if (offset == 0 || compLen == 0 || origLen == 0) {
+    return NULL;
+  }
+
+  if (compLen > woffLen || offset > woffLen - compLen) {
+    FAIL(eWOFF_invalid);
+  }
+
+  data = malloc(origLen);
+  if (!data) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  if (uncompress((Bytef *)data, &origLen,
+                 (const Bytef *)woffData + offset, compLen) != Z_OK ||
+      origLen != READ32BE(header->metaOrigLen)) {
+    FAIL(eWOFF_compression_failure);
+  }
+
+  if (metaLen) {
+    *metaLen = origLen;
+  }
+  if (pStatus) {
+    *pStatus |= status;
+  }
+  return data;
+
+failure:
+  if (data) {
+    free(data);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;    
+}
+
+const uint8_t *
+woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen,
+                   uint32_t * privLen, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint32_t offset, length;
+  uint8_t * data = NULL;
+  uint32_t status = eWOFF_ok;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return NULL;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  header = (const woffHeader *) (woffData);
+
+  offset = READ32BE(header->privOffset);
+  length = READ32BE(header->privLen);
+  if (offset == 0 || length == 0) {
+    return NULL;
+  }
+
+  if (length > woffLen || offset > woffLen - length) {
+    FAIL(eWOFF_invalid);
+  }
+
+  data = malloc(length);
+  if (!data) {
+    FAIL(eWOFF_out_of_memory);
+  }
+
+  memcpy(data, woffData + offset, length);
+
+  if (privLen) {
+    *privLen = length;
+  }
+  if (pStatus) {
+    *pStatus |= status;
+  }
+  return data;
+
+failure:
+  if (data) {
+    free(data);
+  }
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return NULL;    
+}
+
+void
+woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen,
+                   uint16_t * major, uint16_t * minor, uint32_t * pStatus)
+{
+  const woffHeader * header;
+  uint32_t status = eWOFF_ok;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  if (!major || !minor) {
+    FAIL(eWOFF_bad_parameter);
+  }
+
+  *major = *minor = 0;
+
+  header = (const woffHeader *) (woffData);
+
+  *major = READ16BE(header->majorVersion);
+  *minor = READ16BE(header->minorVersion);
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+}
+
+/* utility to print messages corresponding to WOFF encoder/decoder errors */
+void
+woffPrintStatus(FILE * f, uint32_t status, const char * prefix)
+{
+  if (!prefix) {
+    prefix = "";
+  }
+  if (WOFF_WARNING(status)) {
+    const char * template = "%sWOFF warning: %s\n";
+    if (status & eWOFF_warn_unknown_version) {
+      fprintf(f, template, prefix, "unrecognized sfnt version");
+    }
+    if (status & eWOFF_warn_checksum_mismatch) {
+      fprintf(f, template, prefix, "checksum mismatch (corrected)");
+    }
+    if (status & eWOFF_warn_misaligned_table) {
+      fprintf(f, template, prefix, "misaligned font table");
+    }
+    if (status & eWOFF_warn_trailing_data) {
+      fprintf(f, template, prefix, "extraneous input data discarded");
+    }
+    if (status & eWOFF_warn_unpadded_table) {
+      fprintf(f, template, prefix, "final table not correctly padded");
+    }
+    if (status & eWOFF_warn_removed_DSIG) {
+      fprintf(f, template, prefix, "digital signature (DSIG) table removed");
+    }
+  }
+  if (WOFF_FAILURE(status)) {
+    const char * template = "%sWOFF error: %s\n";
+    const char * msg;
+    switch (status & 0xff) {
+    case eWOFF_out_of_memory:
+      msg = "memory allocation failure";
+      break;
+    case eWOFF_invalid:
+      msg = "invalid input font";
+      break;
+    case eWOFF_compression_failure:
+      msg = "zlib compression/decompression failure";
+      break;
+    case eWOFF_bad_signature:
+      msg = "incorrect WOFF file signature";
+      break;
+    case eWOFF_buffer_too_small:
+      msg = "buffer too small";
+      break;
+    case eWOFF_bad_parameter:
+      msg = "bad parameter to WOFF function";
+      break;
+    case eWOFF_illegal_order:
+      msg = "incorrect table directory order";
+      break;
+    default:
+      msg = "unknown internal error";
+      break;
+    }
+    fprintf(f, template, prefix, msg);
+  }
+}
+
+#endif /* not WOFF_MOZILLA_CLIENT */
diff --git a/gfx/thebes/src/woff.h b/gfx/thebes/src/woff.h
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/src/woff.h
@@ -0,0 +1,211 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is WOFF font packaging code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jonathan Kew <jfkthame@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef WOFF_H_
+#define WOFF_H_
+
+/* API for the WOFF encoder and decoder */
+
+#ifdef _MSC_VER /* MS VC lacks inttypes.h
+                   but we can make do with a few definitons here */
+typedef char           int8_t;
+typedef short          int16_t;
+typedef int            int32_t;
+typedef unsigned char  uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+#else
+#include <inttypes.h>
+#endif
+
+#include <stdio.h> /* only for FILE, needed for woffPrintStatus */
+
+/* error codes returned in the status parameter of WOFF functions */
+enum {
+  /* Success */
+  eWOFF_ok = 0,
+
+  /* Errors: no valid result returned */
+  eWOFF_out_of_memory = 1,       /* malloc or realloc failed */
+  eWOFF_invalid = 2,             /* invalid input file (e.g., bad offset) */
+  eWOFF_compression_failure = 3, /* error in zlib call */
+  eWOFF_bad_signature = 4,       /* unrecognized file signature */
+  eWOFF_buffer_too_small = 5,    /* the provided buffer is too small */
+  eWOFF_bad_parameter = 6,       /* bad parameter (e.g., null source ptr) */
+  eWOFF_illegal_order = 7,       /* improperly ordered chunks in WOFF font */
+
+  /* Warnings: call succeeded but something odd was noticed.
+     Multiple warnings may be OR'd together. */
+  eWOFF_warn_unknown_version = 0x0100,   /* unrecognized version of sfnt,
+                                            not standard TrueType or CFF */
+  eWOFF_warn_checksum_mismatch = 0x0200, /* bad checksum, use with caution;
+                                            any DSIG will be invalid */
+  eWOFF_warn_misaligned_table = 0x0400,  /* table not long-aligned; fixing,
+                                            but DSIG will be invalid */
+  eWOFF_warn_trailing_data = 0x0800,     /* trailing junk discarded,
+                                            any DSIG may be invalid */
+  eWOFF_warn_unpadded_table = 0x1000,    /* sfnt not correctly padded,
+                                            any DSIG may be invalid */
+  eWOFF_warn_removed_DSIG = 0x2000       /* removed digital signature
+                                            while fixing checksum errors */
+};
+
+/* Note: status parameters must be initialized to eWOFF_ok before calling
+   WOFF functions. If the status parameter contains an error code,
+   functions will return immediately. */
+
+#define WOFF_SUCCESS(status) (((uint32_t)(status) & 0xff) == eWOFF_ok)
+#define WOFF_FAILURE(status) (!WOFF_SUCCESS(status))
+#define WOFF_WARNING(status) ((uint32_t)(status) & ~0xff)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef WOFF_DISABLE_ENCODING
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the encoded data, or NULL on error;
+ * caller should free() this when finished with it.
+ * Returns length of the encoded data in woffLen.
+ * The new WOFF has no metadata or private block;
+ * see the following functions to update these elements.
+ */
+const uint8_t * woffEncode(const uint8_t * sfntData, uint32_t sfntLen,
+                           uint16_t majorVersion, uint16_t minorVersion,
+                           uint32_t * woffLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Add the given metadata block to the WOFF font, replacing any existing
+ * metadata block. The block will be zlib-compressed.
+ * Metadata is required to be valid XML (use of UTF-8 is recommended),
+ * though this function does not currently check this.
+ * The woffData pointer must be a malloc() block (typically from woffEncode);
+ * it will be freed by this function and a new malloc() block will be returned.
+ * Returns NULL if an error occurs, in which case the original WOFF is NOT freed.
+ */
+const uint8_t * woffSetMetadata(const uint8_t * woffData, uint32_t * woffLen,
+                                const uint8_t * metaData, uint32_t metaLen,
+                                uint32_t * status);
+
+
+/*****************************************************************************
+ * Add the given private data block to the WOFF font, replacing any existing
+ * private block. The block will NOT be zlib-compressed.
+ * Private data may be any arbitrary block of bytes; it may be externally
+ * compressed by the client if desired.
+ * The woffData pointer must be a malloc() block (typically from woffEncode);
+ * it will be freed by this function and a new malloc() block will be returned.
+ * Returns NULL if an error occurs, in which case the original WOFF is NOT freed.
+ */
+const uint8_t * woffSetPrivateData(const uint8_t * woffData, uint32_t * woffLen,
+                                   const uint8_t * privData, uint32_t privLen,
+                                   uint32_t * status);
+
+#endif /* WOFF_DISABLE_ENCODING */
+
+/*****************************************************************************
+ * Returns the size of buffer needed to decode the font (or zero on error).
+ */
+uint32_t woffGetDecodedSize(const uint8_t * woffData, uint32_t woffLen,
+                            uint32_t * pStatus);
+
+
+/*****************************************************************************
+ * Decodes WOFF font to a caller-supplied buffer of size bufferLen.
+ * Returns the actual size of the decoded sfnt data in pActualSfntLen
+ * (must be <= bufferLen, otherwise an error will be returned).
+ */
+void woffDecodeToBuffer(const uint8_t * woffData, uint32_t woffLen,
+                        uint8_t * sfntData, uint32_t bufferLen,
+                        uint32_t * pActualSfntLen, uint32_t * pStatus);
+
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the decoded data, or NULL on error;
+ * caller should free() this when finished with it.
+ * Returns length of the decoded data in sfntLen.
+ */
+const uint8_t * woffDecode(const uint8_t * woffData, uint32_t woffLen,
+                           uint32_t * sfntLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the metadata from the WOFF font,
+ * or NULL if an error occurs or no metadata is present.
+ * Length of the metadata is returned in metaLen.
+ * The metadata is decompressed before returning.
+ */
+const uint8_t * woffGetMetadata(const uint8_t * woffData, uint32_t woffLen,
+                                uint32_t * metaLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Returns a new malloc() block containing the private data from the WOFF font,
+ * or NULL if an error occurs or no private data is present.
+ * Length of the private data is returned in privLen.
+ */
+const uint8_t * woffGetPrivateData(const uint8_t * woffData, uint32_t woffLen,
+                                   uint32_t * privLen, uint32_t * status);
+
+
+/*****************************************************************************
+ * Returns the font version numbers from the WOFF font in the major and minor
+ * parameters.
+ * Check the status result to know if the function succeeded.
+ */
+void woffGetFontVersion(const uint8_t * woffData, uint32_t woffLen,
+                        uint16_t * major, uint16_t * minor,
+                        uint32_t * status);
+
+
+/*****************************************************************************
+ * Utility to print warning and/or error status to the specified FILE*.
+ * The prefix string will be prepended to each line (ok to pass NULL if no
+ * prefix is wanted).
+ * (Provides terse English messages only, not intended for end-user display;
+ * user-friendly tools should map the status codes to their own messages.)
+ */
+void woffPrintStatus(FILE * f, uint32_t status, const char * prefix);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/layout/style/nsFontFaceLoader.cpp b/layout/style/nsFontFaceLoader.cpp
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -125,30 +125,33 @@ nsFontFaceLoader::OnStreamComplete(nsISt
   NS_ASSERTION(ctx && !ctx->PresShell()->IsDestroying(),
                "We should have been canceled already");
 
   // whether an error occurred or not, notify the user font set of the completion
   gfxUserFontSet *userFontSet = ctx->GetUserFontSet();
   if (!userFontSet) {
     return aStatus;
   }
-  
-  PRBool fontUpdate = userFontSet->OnLoadComplete(mFontEntry, aLoader,
+
+  // The userFontSet is responsible for freeing the downloaded data
+  // (aString) when finished with it; the pointer is no longer valid
+  // after OnLoadComplete returns.
+  PRBool fontUpdate = userFontSet->OnLoadComplete(mFontEntry,
                                                   aString, aStringLen,
                                                   aStatus);
 
   // when new font loaded, need to reflow
   if (fontUpdate) {
     // Update layout for the presence of the new font.  Since this is
     // asynchronous, reflows will coalesce.
     ctx->UserFontSetUpdated();
     LOG(("fontdownloader (%p) reflow\n", this));
   }
 
-  return aStatus;
+  return NS_SUCCESS_ADOPTED_DATA;
 }
 
 void
 nsFontFaceLoader::Cancel()
 {
   mFontSet = nsnull;
   mChannel->Cancel(NS_BINDING_ABORTED);
 }
