bug 527276 - part 3 - apply OTS sanitizer to downloaded fonts. r=jdaggett a=blocking2.0
authorJonathan Kew <jfkthame@gmail.com>
Thu, 07 Oct 2010 08:59:19 +0100
changeset 55084 9120bc25cfb72c24c2c8cd2ba1cfeaaba7c413c4
parent 55083 ff826d1ba112c80188264de55a9cecb49aa8f96c
child 55085 4756e676e0fbf6fff95fd649bc3b3153604d3745
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett, blocking2
bugs527276
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 527276 - part 3 - apply OTS sanitizer to downloaded fonts. r=jdaggett a=blocking2.0
gfx/thebes/Makefile.in
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontUtils.cpp
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/woff.c
gfx/thebes/woff.h
modules/libpref/src/init/all.js
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -225,16 +225,17 @@ EXTRA_DSO_LDOPTS += \
 	$(LIBS_DIR) \
 	$(EXTRA_DSO_LIBS) \
 	$(MOZ_UNICHARUTIL_LIBS) \
 	$(XPCOM_LIBS) \
 	$(NSPR_LIBS) \
 	$(ZLIB_LIBS) \
 	$(QCMS_LIBS) \
 	$(MOZ_HARFBUZZ_LIBS) \
+	$(MOZ_OTS_LIBS) \
 	$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 CPPSRCS	+= 	gfxWindowsPlatform.cpp \
 		gfxWindowsSurface.cpp \
 		gfxWindowsNativeDrawing.cpp \
 		nsUnicodeRange.cpp \
 		$(NULL)
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -417,10 +417,16 @@ gfxDWriteFont::GetFontTable(PRUint32 aTa
                                             &data, &size, &context, &exists);
     if (SUCCEEDED(hr) && exists) {
         FontTableRec *ftr = new FontTableRec(mFontFace, context);
         return hb_blob_create(static_cast<const char*>(data), size,
                               HB_MEMORY_MODE_READONLY,
                               DestroyBlobFunc, ftr);
     }
 
+    if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
+        // for downloaded fonts, there may be layout tables cached in the entry
+        // even though they're absent from the sanitized platform font
+        return mFontEntry->GetFontTable(aTag);
+    }
+
     return nsnull;
 }
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -237,16 +237,40 @@ gfxFontEntry::GetFontTable(PRUint32 aTag
 
     if (entry) {
         return hb_blob_reference(entry->GetBlob());
     }
 
     return nsnull;
 }
 
+void
+gfxFontEntry::PreloadFontTable(PRUint32 aTag, nsTArray<PRUint8>& aTable)
+{
+    if (!mFontTableCache.IsInitialized()) {
+        // This is intended for use with downloaded fonts, to cache the layout
+        // tables for harfbuzz, so initialize the cache for 3 entries to allow
+        // for GDEF/GSUB/GPOS.
+        mFontTableCache.Init(3);
+    }
+
+    FontTableCacheEntry *entry = nsnull;
+    if (mFontTableCache.Get(aTag, &entry)) {
+        // this should never happen - it's a logic error in the calling code
+        // (so ignore the fact that we'll leak the elements of aTable here)
+        NS_NOTREACHED("can't preload table, already present in cache!");
+        return;
+    }
+
+    // this adopts the buffer elements of aTable
+    entry = new FontTableCacheEntry(aTable, aTag, mFontTableCache);
+    if (!mFontTableCache.Put(aTag, entry)) {
+        NS_WARNING("failed to cache font table!");
+    }
+}
 
 //////////////////////////////////////////////////////////////////////////////
 //
 // class gfxFontFamily
 //
 //////////////////////////////////////////////////////////////////////////////
 
 // we consider faces with mStandardFace == PR_TRUE to be "greater than" those with PR_FALSE,
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -294,16 +294,21 @@ public:
 
     // Subclasses should override this if they can do something more efficient
     // than getting tables with GetFontTable() and caching them in the entry.
     //
     // Note that some gfxFont implementations may not call this at all,
     // if it is more efficient to get the table from the OS at that level.
     virtual hb_blob_t *GetFontTable(PRUint32 aTag);
 
+    // Preload a font table into the cache (used to store layout tables for
+    // harfbuzz, when they will be stripped from the actual sfnt being
+    // passed to platform font APIs for rasterization)
+    void PreloadFontTable(PRUint32 aTag, nsTArray<PRUint8>& aTable);
+
     nsString         mName;
 
     PRPackedBool     mItalic      : 1;
     PRPackedBool     mFixedPitch  : 1;
     PRPackedBool     mIsProxy     : 1;
     PRPackedBool     mIsValid     : 1;
     PRPackedBool     mIsBadUnderlineFont : 1;
     PRPackedBool     mIsUserFont  : 1;
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -916,31 +916,16 @@ nsresult gfxFontUtils::MakeUniqueUserFon
 }
 
 
 // TrueType/OpenType table handling code
 
 // need byte aligned structs
 #pragma pack(1)
 
-struct SFNTHeader {
-    AutoSwap_PRUint32    sfntVersion;            // Fixed, 0x00010000 for version 1.0.
-    AutoSwap_PRUint16    numTables;              // Number of tables.
-    AutoSwap_PRUint16    searchRange;            // (Maximum power of 2 <= numTables) x 16.
-    AutoSwap_PRUint16    entrySelector;          // Log2(maximum power of 2 <= numTables).
-    AutoSwap_PRUint16    rangeShift;             // NumTables x 16-searchRange.        
-};
-
-struct TableDirEntry {
-    AutoSwap_PRUint32    tag;                    // 4 -byte identifier.
-    AutoSwap_PRUint32    checkSum;               // CheckSum for this table.
-    AutoSwap_PRUint32    offset;                 // Offset from beginning of TrueType font file.
-    AutoSwap_PRUint32    length;                 // Length of this table.        
-};
-
 // name table stores set of name record structures, followed by
 // large block containing all the strings.  name record offset and length
 // indicates the offset and length within that block.
 // http://www.microsoft.com/typography/otspec/name.htm
 struct NameRecordData {
     PRUint32  offset;
     PRUint32  length;
 };
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -350,16 +350,31 @@ struct AutoSwap_PRUint64 {
 
 struct AutoSwap_PRUint24 {
     operator PRUint32() const { return value[0] << 16 | value[1] << 8 | value[2]; }
 private:
     AutoSwap_PRUint24() { }
     PRUint8  value[3];
 };
 
+struct SFNTHeader {
+    AutoSwap_PRUint32    sfntVersion;            // Fixed, 0x00010000 for version 1.0.
+    AutoSwap_PRUint16    numTables;              // Number of tables.
+    AutoSwap_PRUint16    searchRange;            // (Maximum power of 2 <= numTables) x 16.
+    AutoSwap_PRUint16    entrySelector;          // Log2(maximum power of 2 <= numTables).
+    AutoSwap_PRUint16    rangeShift;             // NumTables x 16-searchRange.        
+};
+
+struct TableDirEntry {
+    AutoSwap_PRUint32    tag;                    // 4 -byte identifier.
+    AutoSwap_PRUint32    checkSum;               // CheckSum for this table.
+    AutoSwap_PRUint32    offset;                 // Offset from beginning of TrueType font file.
+    AutoSwap_PRUint32    length;                 // Length of this table.        
+};
+
 struct HeadTable {
     enum {
         HEAD_VERSION = 0x00010000,
         HEAD_MAGIC_NUMBER = 0x5F0F3CF5,
         HEAD_CHECKSUM_CALC_CONST = 0xB1B0AFBA
     };
 
     AutoSwap_PRUint32    tableVersionNumber;    // Fixed, 0x00010000 for version 1.0.
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -413,16 +413,22 @@ gfxMacFont::GetFontTable(PRUint32 aTag)
     CFDataRef dataRef = ::CGFontCopyTableForTag(mCGFont, aTag);
     if (dataRef) {
         return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
                               ::CFDataGetLength(dataRef),
                               HB_MEMORY_MODE_READONLY,
                               DestroyBlobFunc, (void*)dataRef);
     }
 
+    if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
+        // for downloaded fonts, there may be layout tables cached in the entry
+        // even though they're absent from the sanitized platform font
+        return mFontEntry->GetFontTable(aTag);
+    }
+
     return nsnull;
 }
 
 // Try to initialize font metrics via ATS font metrics APIs,
 // and set mIsValid = TRUE on success.
 // We ONLY call this for local (platform) fonts that are not sfnt format;
 // for sfnts, including ALL downloadable fonts, use InitMetricsFromSfntTables
 // because ATSFontGetHorizontalMetrics() has been known to crash when
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -129,16 +129,17 @@ SRGBOverrideObserver::Observe(nsISupport
     NS_ASSERTION(NS_strcmp(someData,
                    NS_LITERAL_STRING("gfx.color_mangement.force_srgb").get()),
                  "Restarting CMS on wrong pref!");
     ShutdownCMS();
     return NS_OK;
 }
 
 #define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
+#define GFX_DOWNLOADABLE_FONTS_SANITIZE "gfx.downloadable_fonts.sanitize"
 
 #define GFX_PREF_HARFBUZZ_LEVEL "gfx.font_rendering.harfbuzz.level"
 #define HARFBUZZ_LEVEL_DEFAULT  0
 
 class FontPrefsObserver : public nsIObserver
 {
 public:
     NS_DECL_ISUPPORTS
@@ -202,16 +203,17 @@ static const char *gPrefLangNames[] = {
     "x-unicode",
     "x-user-def"
 };
 
 gfxPlatform::gfxPlatform()
 {
     mUseHarfBuzzLevel = UNINITIALIZED_VALUE;
     mAllowDownloadableFonts = UNINITIALIZED_VALUE;
+    mDownloadableFontsSanitize = UNINITIALIZED_VALUE;
 }
 
 gfxPlatform*
 gfxPlatform::GetPlatform()
 {
     return gPlatform;
 }
 
@@ -297,17 +299,17 @@ gfxPlatform::Init()
 
     /* Create and register our CMS Override observer. */
     gPlatform->overrideObserver = new SRGBOverrideObserver();
     FontPrefsObserver *fontPrefObserver = new FontPrefsObserver();
 
     nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
         prefs->AddObserver(CMForceSRGBPrefName, gPlatform->overrideObserver, PR_TRUE);
-        prefs->AddObserver(GFX_DOWNLOADABLE_FONTS_ENABLED, fontPrefObserver, PR_FALSE);
+        prefs->AddObserver("gfx.downloadable_fonts.", fontPrefObserver, PR_FALSE);
         prefs->AddObserver("gfx.font_rendering.", fontPrefObserver, PR_FALSE);
     }
 
     return NS_OK;
 }
 
 void
 gfxPlatform::Shutdown()
@@ -410,22 +412,34 @@ gfxPlatform::GetBoolPref(const char *aPr
 
     return aDefault;
 }
 
 PRBool
 gfxPlatform::DownloadableFontsEnabled()
 {
     if (mAllowDownloadableFonts == UNINITIALIZED_VALUE) {
-        mAllowDownloadableFonts = GetBoolPref(GFX_DOWNLOADABLE_FONTS_ENABLED, PR_FALSE);
+        mAllowDownloadableFonts =
+            GetBoolPref(GFX_DOWNLOADABLE_FONTS_ENABLED, PR_FALSE);
     }
 
     return mAllowDownloadableFonts;
 }
 
+PRBool
+gfxPlatform::SanitizeDownloadedFonts()
+{
+    if (mDownloadableFontsSanitize == UNINITIALIZED_VALUE) {
+        mDownloadableFontsSanitize =
+            GetBoolPref(GFX_DOWNLOADABLE_FONTS_SANITIZE, PR_TRUE);
+    }
+
+    return mDownloadableFontsSanitize;
+}
+
 PRInt8
 gfxPlatform::UseHarfBuzzLevel()
 {
     if (mUseHarfBuzzLevel == UNINITIALIZED_VALUE) {
         mUseHarfBuzzLevel = HARFBUZZ_LEVEL_DEFAULT;
         nsCOMPtr<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
         if (prefs) {
             PRInt32 level;
@@ -1139,14 +1153,16 @@ gfxPlatform::SetupClusterBoundaries(gfxT
 }
 
 void
 gfxPlatform::FontsPrefsChanged(nsIPrefBranch *aPrefBranch, const char *aPref)
 {
     NS_ASSERTION(aPref != nsnull, "null preference");
     if (!strcmp(GFX_DOWNLOADABLE_FONTS_ENABLED, aPref)) {
         mAllowDownloadableFonts = UNINITIALIZED_VALUE;
+    } else if (!strcmp(GFX_DOWNLOADABLE_FONTS_SANITIZE, aPref)) {
+        mDownloadableFontsSanitize = UNINITIALIZED_VALUE;
     } else if (!strcmp(GFX_PREF_HARFBUZZ_LEVEL, aPref)) {
         mUseHarfBuzzLevel = UNINITIALIZED_VALUE;
         gfxTextRunWordCache::Flush();
         gfxFontCache::GetCache()->AgeAllGenerations();
     }
 }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -228,17 +228,22 @@ public:
      */
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
                                            const PRUint8 *aFontData,
                                            PRUint32 aLength);
 
     /**
      * Whether to allow downloadable fonts via @font-face rules
      */
-    virtual PRBool DownloadableFontsEnabled();
+    PRBool DownloadableFontsEnabled();
+
+    /**
+     * Whether to sanitize downloaded fonts using the OTS library
+     */
+    PRBool SanitizeDownloadedFonts();
 
     /**
      * Whether to use the harfbuzz shaper (depending on script complexity).
      *
      * This allows harfbuzz to be enabled selectively via the preferences.
      * Current "harfbuzz level" options:
      * <= 0 will never use the harfbuzz shaper;
      *  = 1 will use it for "simple" scripts (Latin, Cyrillic, CJK, etc);
@@ -350,16 +355,17 @@ protected:
     virtual ~gfxPlatform();
 
     static PRBool GetBoolPref(const char *aPref, PRBool aDefault);
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], PRUint32 &aLen, 
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
                                                
     PRBool  mAllowDownloadableFonts;
+    PRBool  mDownloadableFontsSanitize;
 
     // whether to use the HarfBuzz layout engine
     PRInt8  mUseHarfBuzzLevel;
 
 private:
     virtual qcms_profile* GetPlatformCMSOutputProfile();
 
     nsRefPtr<gfxASurface> mScreenReferenceSurface;
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -44,16 +44,21 @@
 #include "gfxUserFontSet.h"
 #include "gfxPlatform.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "prlong.h"
 
 #include "woff.h"
 
+#include "opentype-sanitiser.h"
+#include "ots-memory-stream.h"
+
+using namespace mozilla;
+
 #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);
@@ -196,17 +201,17 @@ gfxUserFontSet::FindFontEntry(const nsAS
 }
 
 // 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*
+static const PRUint8*
 PrepareOpenTypeData(const PRUint8* aData, PRUint32* aLength)
 {
     switch(gfxFontUtils::DetermineFontDataType(aData, *aLength)) {
     
     case GFX_USERFONT_OPENTYPE:
         // nothing to do
         return aData;
         
@@ -242,65 +247,292 @@ PrepareOpenTypeData(const PRUint8* aData
     }
 
     // discard downloaded data that couldn't be used
     NS_Free((void*)aData);
 
     return nsnull;
 }
 
+// Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
+// adapted to use Mozilla allocators and to allow the final
+// memory buffer to be adopted by the client.
+class ExpandingMemoryStream : public ots::OTSStream {
+public:
+    ExpandingMemoryStream(size_t initial, size_t limit)
+        : mLength(initial), mLimit(limit), mOff(0) {
+        mPtr = NS_Alloc(mLength);
+    }
+
+    ~ExpandingMemoryStream() {
+        NS_Free(mPtr);
+    }
+
+    // return the buffer, and give up ownership of it
+    // so the caller becomes responsible to call NS_Free
+    // when finished with it
+    void* forget() {
+        void* p = mPtr;
+        mPtr = nsnull;
+        return p;
+    }
+
+    bool WriteRaw(const void *data, size_t length) {
+        if ((mOff + length > mLength) ||
+            (mLength > std::numeric_limits<size_t>::max() - mOff)) {
+            if (mLength == mLimit) {
+                return false;
+            }
+            size_t newLength = (mLength + 1) * 2;
+            if (newLength < mLength) {
+                return false;
+            }
+            if (newLength > mLimit) {
+                newLength = mLimit;
+            }
+            mPtr = NS_Realloc(mPtr, newLength);
+            mLength = newLength;
+            return WriteRaw(data, length);
+        }
+        std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
+        mOff += length;
+        return true;
+    }
+
+    bool Seek(off_t position) {
+        if (position < 0) {
+            return false;
+        }
+        if (static_cast<size_t>(position) > mLength) {
+            return false;
+        }
+        mOff = position;
+        return true;
+    }
+
+    off_t Tell() const {
+        return mOff;
+    }
+
+private:
+    void*        mPtr;
+    size_t       mLength;
+    const size_t mLimit;
+    off_t        mOff;
+};
+
+// Call the OTS library to sanitize an sfnt before attempting to use it.
+// Returns a newly-allocated block, or NULL in case of fatal errors.
+static const PRUint8*
+SanitizeOpenTypeData(const PRUint8* aData, PRUint32 aLength,
+                     PRUint32& aSaneLength, bool aIsCompressed)
+{
+    // limit output/expansion to 256MB
+    ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength,
+                                 1024 * 1024 * 256);
+    if (ots::Process(&output, aData, aLength)) {
+        aSaneLength = output.Tell();
+        return static_cast<PRUint8*>(output.forget());
+    } else {
+        aSaneLength = 0;
+        return nsnull;
+    }
+}
+
+// Find the GDEF, GSUB, GPOS tables in aFontData (if present)
+// and cache copies in the given font entry.
+// The sfnt table directory has already been accepted by the OTS
+// sanitizer before this is called, so we can assume entries are valid.
+//
+// This is a temporary workaround until OTS has full support for the
+// G*** tables, so that they can safely be left in the main font.
+// When http://code.google.com/p/chromium/issues/detail?id=27131 gets fixed,
+// we should remove this hack.
+static void
+CacheLayoutTablesFromSFNT(const PRUint8* aFontData, PRUint32 aLength,
+                          gfxFontEntry* aFontEntry)
+{
+    const SFNTHeader *sfntHeader = reinterpret_cast<const SFNTHeader*>(aFontData);
+    PRUint16 numTables = sfntHeader->numTables;
+    
+    // table directory entries begin immediately following SFNT header
+    const TableDirEntry *dirEntry = 
+        reinterpret_cast<const TableDirEntry*>(aFontData + sizeof(SFNTHeader));
+
+    while (numTables-- > 0) {
+        switch (dirEntry->tag) {
+        case TRUETYPE_TAG('G','D','E','F'):
+        case TRUETYPE_TAG('G','P','O','S'):
+        case TRUETYPE_TAG('G','S','U','B'): {
+                nsTArray<PRUint8> buffer;
+                if (!buffer.AppendElements(aFontData + dirEntry->offset,
+                                           dirEntry->length)) {
+                    NS_WARNING("failed to cache font table - out of memory?");
+                    break;
+                }
+                aFontEntry->PreloadFontTable(dirEntry->tag, buffer);
+            }
+            break;
+
+        default:
+            if (dirEntry->tag > TRUETYPE_TAG('G','S','U','B')) {
+                // directory entries are required to be sorted,
+                // so we can terminate as soon as we find a tag > 'GSUB'
+                numTables = 0;
+            }
+            break;
+        }
+        ++dirEntry;
+    }
+}
+
+// OTS drops the OT Layout tables when decoding a WOFF file, so retrieve them
+// separately and cache them (unchecked) in the font entry; harfbuzz will
+// sanitize them when it needs to use them.
+static void
+PreloadTableFromWOFF(const PRUint8* aFontData, PRUint32 aLength,
+                     PRUint32 aTableTag, gfxFontEntry* aFontEntry)
+{
+    PRUint32 status = eWOFF_ok;
+    PRUint32 len = woffGetTableSize(aFontData, aLength, aTableTag, &status);
+    if (WOFF_SUCCESS(status) && len > 0) {
+        nsTArray<PRUint8> buffer;
+        if (!buffer.AppendElements(len)) {
+            NS_WARNING("failed to cache font table - out of memory?");
+            return;
+        }
+        woffGetTableToBuffer(aFontData, aLength, aTableTag,
+                             buffer.Elements(), buffer.Length(),
+                             &len, &status);
+        if (WOFF_FAILURE(status)) {
+            NS_WARNING("failed to cache font table - WOFF decoding error?");
+            return;
+        }
+        aFontEntry->PreloadFontTable(aTableTag, buffer);
+    }
+}
+
+static void
+CacheLayoutTablesFromWOFF(const PRUint8* aFontData, PRUint32 aLength,
+                          gfxFontEntry* aFontEntry)
+{
+    PreloadTableFromWOFF(aFontData, aLength, TRUETYPE_TAG('G','D','E','F'),
+                         aFontEntry);
+    PreloadTableFromWOFF(aFontData, aLength, TRUETYPE_TAG('G','P','O','S'),
+                         aFontEntry);
+    PreloadTableFromWOFF(aFontData, aLength, TRUETYPE_TAG('G','S','U','B'),
+                         aFontEntry);
+}
+
 // 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,
                                const PRUint8 *aFontData, PRUint32 aLength, 
                                nsresult aDownloadStatus)
 {
-    NS_ASSERTION(aFontToLoad->mIsProxy, "trying to load font data for wrong font entry type");
+    NS_ASSERTION(aFontToLoad->mIsProxy,
+                 "trying to load font data for wrong font entry type");
 
     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 = nsnull;
 
-        // Unwrap/decompress or otherwise munge the downloaded data
+        // Unwrap/decompress/sanitize 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);
-            if (fe) {
-                if (pe->mFeatureSettings) {
-                    fe->mFeatureSettings = new nsTArray<gfxFontFeature>;
-                    fe->mFeatureSettings->AppendElements(*pe->mFeatureSettings);
+
+        if (gfxPlatform::GetPlatform()->SanitizeDownloadedFonts()) {
+            gfxUserFontType fontType =
+                gfxFontUtils::DetermineFontDataType(aFontData, aLength);
+
+            // Call the OTS sanitizer; this will also decode WOFF to sfnt
+            // if necessary. The original data in aFontData is left unchanged.
+            PRUint32 saneLen;
+            const PRUint8* saneData =
+                SanitizeOpenTypeData(aFontData, aLength, saneLen,
+                                     fontType == GFX_USERFONT_WOFF);
+#ifdef DEBUG
+            if (!saneData) {
+                char buf[1000];
+                sprintf(buf, "downloaded font rejected for \"%s\"",
+                        NS_ConvertUTF16toUTF8(pe->FamilyName()).get());
+                NS_WARNING(buf);
+            }
+#endif
+            if (saneData) {
+                // Here ownership of saneData is passed to the platform,
+                // which will delete it when no longer required
+                fe = gfxPlatform::GetPlatform()->MakePlatformFont(pe,
+                                                                  saneData,
+                                                                  saneLen);
+                if (fe) {
+                    // if aFontData includes OpenType layout tables, we need to
+                    // cache them in the font entry for harfbuzz to use,
+                    // as they will have been dropped from the sanitized sfnt
+                    // (temporary hack, see CacheLayoutTablesFromSFNT)
+                    switch (fontType) {
+                    case GFX_USERFONT_OPENTYPE:
+                        CacheLayoutTablesFromSFNT(aFontData, aLength, fe);
+                        break;
+
+                    case GFX_USERFONT_WOFF:
+                        CacheLayoutTablesFromWOFF(aFontData, aLength, fe);
+                        break;
+
+                    default:
+                        break;
+                    }
+                } else {
+                    NS_WARNING("failed to make platform font from download");
                 }
-                fe->mLanguageOverride = pe->mLanguageOverride;
             }
-            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)
+            // FIXME: this code can be removed once we remove the pref to
+            // disable the sanitizer; the PrepareOpenTypeData and
+            // ValidateSFNTHeaders functions will then be obsolete.
+            aFontData = PrepareOpenTypeData(aFontData, &aLength);
+
             if (aFontData) {
-                NS_Free((void*)aFontData);
+                if (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; // we must NOT free this below!
+                } else {
+                    // the data was unusable, so just discard it
+                    // (error will be reported below, if logging is enabled)
+                    NS_WARNING("failed to make platform font from download");
+                }
             }
         }
 
+        if (aFontData) {
+            NS_Free((void*)aFontData);
+            aFontData = nsnull;
+        }
+
         if (fe) {
+            // copy OpenType feature/language settings from the proxy to the
+            // newly-created font entry
+            if (pe->mFeatureSettings) {
+                fe->mFeatureSettings = new nsTArray<gfxFontFeature>;
+                fe->mFeatureSettings->AppendElements(*pe->mFeatureSettings);
+            }
+            fe->mLanguageOverride = pe->mLanguageOverride;
+
             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(),
@@ -317,31 +549,32 @@ gfxUserFontSet::OnLoadComplete(gfxFontEn
                 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(),
                  aDownloadStatus));
         }
 #endif
     }
 
+    if (aFontData) {
+        NS_Free((void*)aFontData);
+    }
+
     // error occurred, load next src
     LoadStatus status;
 
     status = LoadNext(pe);
     if (status == STATUS_LOADED) {
         // load may succeed if external font resource followed by
         // local font, in this case need to bump generation
         IncrementGeneration();
--- a/gfx/thebes/woff.c
+++ b/gfx/thebes/woff.c
@@ -993,16 +993,132 @@ failure:
     free(sfntData);
   }
   if (pStatus) {
     *pStatus = status;
   }
   return NULL;
 }
 
+/* functions to get size and data of a single table */
+
+uint32_t woffGetTableSize(const uint8_t * woffData, uint32_t woffLen,
+                          uint32_t tag, uint32_t * pStatus)
+{
+  uint32_t status = eWOFF_ok;
+  const woffHeader * header;
+  uint16_t numTables;
+  uint16_t tableIndex;
+  const woffDirEntry * woffDir;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return 0;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  header = (const woffHeader *) (woffData);
+
+  numTables = READ16BE(header->numTables);
+  woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+
+  for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+    uint32_t thisTag;
+    thisTag = READ32BE(woffDir[tableIndex].tag);
+    if (thisTag < tag) {
+      continue;
+    }
+    if (thisTag > tag) {
+      break;
+    }
+    return READ32BE(woffDir[tableIndex].origLen);
+  }
+
+  status = eWOFF_warn_no_such_table;
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+  return 0;
+}
+
+void woffGetTableToBuffer(const uint8_t * woffData, uint32_t woffLen,
+                          uint32_t tag, uint8_t * buffer, uint32_t bufferLen,
+                          uint32_t * pTableLen, uint32_t * pStatus)
+{
+  uint32_t status = eWOFF_ok;
+  const woffHeader * header;
+  uint16_t numTables;
+  uint16_t tableIndex;
+  const woffDirEntry * woffDir;
+
+  if (pStatus && WOFF_FAILURE(*pStatus)) {
+    return;
+  }
+
+  status = sanityCheck(woffData, woffLen);
+  if (WOFF_FAILURE(status)) {
+    FAIL(status);
+  }
+
+  header = (const woffHeader *) (woffData);
+
+  numTables = READ16BE(header->numTables);
+  woffDir = (const woffDirEntry *) (woffData + sizeof(woffHeader));
+
+  for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+    uint32_t thisTag, origLen, compLen, sourceOffset;
+    thisTag = READ32BE(woffDir[tableIndex].tag);
+    if (thisTag < tag) {
+      continue;
+    }
+    if (thisTag > tag) {
+      break;
+    }
+
+    /* found the required table: decompress it (checking for overflow) */
+    origLen = READ32BE(woffDir[tableIndex].origLen);
+    if (origLen > bufferLen) {
+      FAIL(eWOFF_buffer_too_small);
+    }
+
+    compLen = READ32BE(woffDir[tableIndex].compLen);
+    sourceOffset = READ32BE(woffDir[tableIndex].offset);
+
+    if (compLen < origLen) {
+      uLongf destLen = origLen;
+      if (uncompress((Bytef *)(buffer), &destLen,
+                     (const Bytef *)(woffData + sourceOffset),
+                     compLen) != Z_OK || destLen != origLen) {
+        FAIL(eWOFF_compression_failure);
+      }
+    } else {
+      memcpy(buffer, woffData + sourceOffset, origLen);
+    }
+
+    if (pTableLen) {
+      *pTableLen = origLen;
+    }
+
+    return;
+  }
+
+  status = eWOFF_warn_no_such_table;
+
+failure:
+  if (pStatus) {
+    *pStatus = status;
+  }
+}
+
+
 #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;
--- a/gfx/thebes/woff.h
+++ b/gfx/thebes/woff.h
@@ -37,17 +37,17 @@
 
 #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 signed 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;
 typedef unsigned __int64  uint64_t;
 #else
 #include <inttypes.h>
@@ -76,18 +76,19 @@ enum {
   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
+  eWOFF_warn_removed_DSIG = 0x2000,      /* removed digital signature
                                             while fixing checksum errors */
+  eWOFF_warn_no_such_table = 0x4000      /* specified table not present */
 };
 
 /* 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))
@@ -162,16 +163,33 @@ void woffDecodeToBuffer(const uint8_t * 
  * 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 the size of buffer needed for a specific table (or zero on error).
+ */
+uint32_t woffGetTableSize(const uint8_t * woffData, uint32_t woffLen,
+                          uint32_t tag, uint32_t * pStatus);
+
+
+/*****************************************************************************
+ * Gets a table from a WOFF font to a caller-supplied buffer of size bufferLen.
+ * Returns the actual size of the decoded table in pTableLen
+ * (must be <= bufferLen, otherwise an error will be returned).
+ */
+void woffGetTableToBuffer(const uint8_t * woffData, uint32_t woffLen,
+                          uint32_t tag, uint8_t * buffer, uint32_t bufferLen,
+                          uint32_t * pTableLen, uint32_t * pStatus);
+
+
+/*****************************************************************************
  * 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);
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -187,16 +187,17 @@ pref("media.autoplay.enabled", true);
 // See eCMSMode in gfx/thebes/gfxPlatform.h
 pref("gfx.color_management.mode", 2);
 pref("gfx.color_management.display_profile", "");
 pref("gfx.color_management.rendering_intent", 0);
 
 pref("gfx.3d_video.enabled", false);
 
 pref("gfx.downloadable_fonts.enabled", true);
+pref("gfx.downloadable_fonts.sanitize", true);
 
 pref("gfx.font_rendering.harfbuzz.level", 1);
 
 #ifdef XP_WIN
 #ifndef WINCE
 pref("gfx.font_rendering.directwrite.enabled", false);
 #endif
 #endif