From 7306315f298d004cf55caa1661970cf3c78be104 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Sun, 21 Dec 2008 02:26:18 +0100 Subject: [PATCH] Bug 463806 - [PATCH][@font-face] Downloaded font activation on Mac may fail due to ATS cache corruption; r=(jdaggett + roc) sr=roc --- gfx/thebes/src/gfxQuartzFontCache.h | 4 +- gfx/thebes/src/gfxQuartzFontCache.mm | 185 +++++++++++++++------------ 2 files changed, 109 insertions(+), 80 deletions(-) diff --git a/gfx/thebes/src/gfxQuartzFontCache.h b/gfx/thebes/src/gfxQuartzFontCache.h index 802a895f776d..0be3a16b5f37 100644 --- a/gfx/thebes/src/gfxQuartzFontCache.h +++ b/gfx/thebes/src/gfxQuartzFontCache.h @@ -85,7 +85,9 @@ public: protected: // for use with data fonts - MacOSFontEntry(ATSUFontID aFontID, PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle, gfxUserFontData *aUserFontData); + MacOSFontEntry(const nsAString& aPostscriptName, ATSUFontID aFontID, + PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle, + gfxUserFontData *aUserFontData); PRUint32 mTraits; MacOSFamilyEntry *mFamily; diff --git a/gfx/thebes/src/gfxQuartzFontCache.mm b/gfx/thebes/src/gfxQuartzFontCache.mm index 2461a91b79db..a47eeacb7f66 100644 --- a/gfx/thebes/src/gfxQuartzFontCache.mm +++ b/gfx/thebes/src/gfxQuartzFontCache.mm @@ -2,12 +2,13 @@ * ***** BEGIN LICENSE BLOCK ***** * Version: BSD * - * Copyright (C) 2006 Mozilla Corporation. All rights reserved. + * Copyright (C) 2006-2008 Mozilla Corporation. All rights reserved. * * Contributor(s): * Vladimir Vukicevic * Masayuki Nakano * John Daggett + * Jonathan Kew * * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * @@ -117,7 +118,7 @@ gfxQuartzFontCache::GenerateFontListKey(const nsAString& aKeyName, nsAString& aR #pragma mark- MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, - PRInt32 aAppleWeight, PRUint32 aTraits, MacOSFamilyEntry *aFamily) + PRInt32 aAppleWeight, PRUint32 aTraits, MacOSFamilyEntry *aFamily) : gfxFontEntry(aPostscriptName), mTraits(aTraits), mFamily(aFamily), mATSUFontID(0), mATSUIDInitialized(0) { @@ -127,10 +128,12 @@ MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, mFixedPitch = (mTraits & NSFixedPitchFontMask ? 1 : 0); } -MacOSFontEntry::MacOSFontEntry(ATSUFontID aFontID, PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle, gfxUserFontData *aUserFontData) +MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, ATSUFontID aFontID, + PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle, + gfxUserFontData *aUserFontData) { // xxx - stretch is basically ignored for now - + mATSUIDInitialized = PR_TRUE; mATSUFontID = aFontID; mUserFontData = aUserFontData; @@ -143,23 +146,7 @@ MacOSFontEntry::MacOSFontEntry(ATSUFontID aFontID, PRUint16 aWeight, PRUint16 aS (mFixedPitch ? NSFixedPitchFontMask : 0) | (mWeight >= 600 ? NSBoldFontMask : NSUnboldFontMask); - // get the postscript name - OSStatus err; - NSString *psname = NULL; - - // now lookup the Postscript name - err = ATSFontGetPostScriptName((ATSFontRef) aFontID, kATSOptionFlagsDefault, (CFStringRef*) (&psname)); - if (err == noErr) { - GetStringForNSString(psname, mName); - [psname release]; - } else { - mIsValid = PR_FALSE; -#ifdef DEBUG - char warnBuf[1024]; - sprintf(warnBuf, "ATSFontGetPostScriptName err = %d", (PRInt32)err); - NS_WARNING(warnBuf); -#endif - } + mName = aPostscriptName; } const nsString& @@ -706,7 +693,7 @@ const PRUint32 kNonNormalTraits = NSItalicFontMask | NSBoldFontMask | NSNarrowFo void gfxQuartzFontCache::InitFontList() { - ATSGeneration currentGeneration = ATSGeneration(); + ATSGeneration currentGeneration = ATSGetGeneration(); // need to ignore notifications after adding each font if (mATSGeneration == currentGeneration) @@ -1338,73 +1325,106 @@ gfxQuartzFontCache::MakePlatformFont(const gfxFontEntry *aProxyEntry, return nsnull; } - ATSUFontID fontID; + ATSFontRef fontRef; ATSFontContainerRef containerRef; - err = ATSFontActivateFromMemory(const_cast(aFontData), aLength, - kPrivateATSFontContextPrivate, - kATSFontFormatUnspecified, - NULL, - kATSOptionFlagsDoNotNotify, - &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(aFontData), aLength, + kPrivateATSFontContextPrivate, + kATSFontFormatUnspecified, + NULL, + kATSOptionFlagsDoNotNotify, + &containerRef); + mATSGeneration = ATSGetGeneration(); - if (err != noErr) { + if (err != noErr) { #if DEBUG - char warnBuf[1024]; - const gfxProxyFontEntry *proxyEntry = - static_cast (aProxyEntry); - sprintf(warnBuf, "downloaded font error, ATSFontActivateFromMemory err: %d for (%s)", - PRInt32(err), - NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get()); - NS_WARNING(warnBuf); + char warnBuf[1024]; + const gfxProxyFontEntry *proxyEntry = + static_cast (aProxyEntry); + sprintf(warnBuf, "downloaded font error, ATSFontActivateFromMemory err: %d for (%s)", + PRInt32(err), + NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get()); + NS_WARNING(warnBuf); #endif - return nsnull; - } + return nsnull; + } - mATSGeneration = ATSGeneration(); - - // ignoring containers with multiple fonts, use the first face only for now - err = ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, - (ATSFontRef*)&fontID, NULL); - if (err != noErr) { + // ignoring containers with multiple fonts, use the first face only for now + err = ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, + &fontRef, NULL); + if (err != noErr) { #if DEBUG - char warnBuf[1024]; - const gfxProxyFontEntry *proxyEntry = - static_cast (aProxyEntry); - sprintf(warnBuf, "downloaded font error, ATSFontFindFromContainer err: %d for (%s)", - PRInt32(err), - NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get()); - NS_WARNING(warnBuf); + char warnBuf[1024]; + const gfxProxyFontEntry *proxyEntry = + static_cast (aProxyEntry); + sprintf(warnBuf, "downloaded font error, ATSFontFindFromContainer err: %d for (%s)", + PRInt32(err), + NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get()); + NS_WARNING(warnBuf); #endif - ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); - return nsnull; - } + ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); + return nsnull; + } - // font entry will own this - MacOSUserFontData *userFontData = new MacOSUserFontData(containerRef); - - if (!userFontData) { - ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); - return nsnull; - } + // now lookup the Postscript name; this may fail if the font cache is bad + OSStatus err; + NSString *psname = NULL; + nsAutoString postscriptName; + err = ATSFontGetPostScriptName(fontRef, kATSOptionFlagsDefault, (CFStringRef*) (&psname)); + if (err == noErr) { + GetStringForNSString(psname, postscriptName); + [psname release]; + } else { +#ifdef DEBUG + char warnBuf[1024]; + const gfxProxyFontEntry *proxyEntry = + static_cast (aProxyEntry); + sprintf(warnBuf, "ATSFontGetPostScriptName err = %d for (%s), retries = %d", (PRInt32)err, + NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get(), retryCount); + NS_WARNING(warnBuf); +#endif + ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); + // retry the activation a couple of times if this fails + // (may be a transient failure due to ATS font cache issues) + continue; + } - PRUint16 w = aProxyEntry->mWeight; - NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!"); + // font entry will own this + MacOSUserFontData *userFontData = new MacOSUserFontData(containerRef); - MacOSFontEntry *newFontEntry = - new MacOSFontEntry(fontID, w, aProxyEntry->mStretch, - (PRUint32(aProxyEntry->mItalic) ? - FONT_STYLE_ITALIC : - FONT_STYLE_NORMAL), - userFontData); + if (!userFontData) { + ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); + return nsnull; + } - if (!newFontEntry) { - delete userFontData; - return nsnull; - } - - // if something is funky about this font, delete immediately - if (newFontEntry && !newFontEntry->mIsValid) { + PRUint16 w = aProxyEntry->mWeight; + NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!"); + + // create the font entry + MacOSFontEntry *newFontEntry = + new MacOSFontEntry(postscriptName, + FMGetFontFromATSFontRef(fontRef), + w, aProxyEntry->mStretch, + (PRUint32(aProxyEntry->mItalic) ? + FONT_STYLE_ITALIC : + FONT_STYLE_NORMAL), + userFontData); + + if (!newFontEntry) { + delete userFontData; + return nsnull; + } + + // if we succeeded (which should always be the case), return the new font + if (newFontEntry->mIsValid) + return newFontEntry; + + // if something is funky about this font, delete immediately #if DEBUG char warnBuf[1024]; const gfxProxyFontEntry *proxyEntry = @@ -1414,10 +1434,17 @@ gfxQuartzFontCache::MakePlatformFont(const gfxFontEntry *aProxyEntry, NS_WARNING(warnBuf); #endif delete newFontEntry; - return nsnull; + + // We don't retry from here; the ATS font cache issue would have caused failure earlier + // so if we get here, there's something else bad going on within our font data structures. + // Currently, there should be no way to reach here, as fontentry creation cannot fail + // except by memory allocation failure. + NS_WARNING("invalid font entry for a newly activated font"); + break; } - return newFontEntry; + // if we get here, the activation failed (even with possible retries); can't use this font + return nsnull; }