gfx/thebes/gfxFT2FontList.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/ArrayUtils.h"
     7 #include "mozilla/MemoryReporting.h"
     9 #if (MOZ_WIDGET_GTK == 2)
    10 #include "gfxPlatformGtk.h"
    11 #define gfxToolkitPlatform gfxPlatformGtk
    12 #elif defined(MOZ_WIDGET_QT)
    13 #include <qfontinfo.h>
    14 #include "gfxQtPlatform.h"
    15 #define gfxToolkitPlatform gfxQtPlatform
    16 #elif defined(XP_WIN)
    17 #include "gfxWindowsPlatform.h"
    18 #define gfxToolkitPlatform gfxWindowsPlatform
    19 #elif defined(ANDROID)
    20 #include "mozilla/dom/ContentChild.h"
    21 #include "gfxAndroidPlatform.h"
    22 #include "mozilla/Omnijar.h"
    23 #include "nsIInputStream.h"
    24 #include "nsNetUtil.h"
    25 #define gfxToolkitPlatform gfxAndroidPlatform
    26 #endif
    28 #ifdef ANDROID
    29 #include "nsXULAppAPI.h"
    30 #include <dirent.h>
    31 #include <android/log.h>
    32 #define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
    33 #endif
    35 #include "ft2build.h"
    36 #include FT_FREETYPE_H
    37 #include FT_TRUETYPE_TAGS_H
    38 #include FT_TRUETYPE_TABLES_H
    39 #include "cairo-ft.h"
    41 #include "gfxFT2FontList.h"
    42 #include "gfxFT2Fonts.h"
    43 #include "gfxUserFontSet.h"
    44 #include "gfxFontUtils.h"
    46 #include "nsServiceManagerUtils.h"
    47 #include "nsTArray.h"
    48 #include "nsUnicharUtils.h"
    50 #include "nsDirectoryServiceUtils.h"
    51 #include "nsDirectoryServiceDefs.h"
    52 #include "nsAppDirectoryServiceDefs.h"
    53 #include "nsISimpleEnumerator.h"
    54 #include "nsIMemory.h"
    55 #include "gfxFontConstants.h"
    57 #include "mozilla/Preferences.h"
    58 #include "mozilla/scache/StartupCache.h"
    59 #include <sys/stat.h>
    61 #ifdef XP_WIN
    62 #include "nsIWindowsRegKey.h"
    63 #include <windows.h>
    64 #endif
    66 using namespace mozilla;
    68 #ifdef PR_LOGGING
    69 static PRLogModuleInfo *
    70 GetFontInfoLog()
    71 {
    72     static PRLogModuleInfo *sLog;
    73     if (!sLog)
    74         sLog = PR_NewLogModule("fontInfoLog");
    75     return sLog;
    76 }
    77 #endif /* PR_LOGGING */
    79 #undef LOG
    80 #define LOG(args) PR_LOG(GetFontInfoLog(), PR_LOG_DEBUG, args)
    81 #define LOG_ENABLED() PR_LOG_TEST(GetFontInfoLog(), PR_LOG_DEBUG)
    83 static cairo_user_data_key_t sFTUserFontDataKey;
    85 static __inline void
    86 BuildKeyNameFromFontName(nsAString &aName)
    87 {
    88 #ifdef XP_WIN
    89     if (aName.Length() >= LF_FACESIZE)
    90         aName.Truncate(LF_FACESIZE - 1);
    91 #endif
    92     ToLowerCase(aName);
    93 }
    95 // Helper to access the FT_Face for a given FT2FontEntry,
    96 // creating a temporary face if the entry does not have one yet.
    97 // This allows us to read font names, tables, etc if necessary
    98 // without permanently instantiating a freetype face and consuming
    99 // memory long-term.
   100 // This may fail (resulting in a null FT_Face), e.g. if it fails to
   101 // allocate memory to uncompress a font from omnijar.
   102 class AutoFTFace {
   103 public:
   104     AutoFTFace(FT2FontEntry* aFontEntry)
   105         : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
   106     {
   107         if (aFontEntry->mFTFace) {
   108             mFace = aFontEntry->mFTFace;
   109             return;
   110         }
   112         NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
   113                      "can't use AutoFTFace for fonts without a filename");
   114         FT_Library ft = gfxToolkitPlatform::GetPlatform()->GetFTLibrary();
   116         // A relative path (no initial "/") means this is a resource in
   117         // omnijar, not an installed font on the device.
   118         // The NS_ASSERTIONs here should never fail, as the resource must have
   119         // been read successfully during font-list initialization or we'd never
   120         // have created the font entry. The only legitimate runtime failure
   121         // here would be memory allocation, in which case mFace remains null.
   122         if (aFontEntry->mFilename[0] != '/') {
   123             nsRefPtr<nsZipArchive> reader =
   124                 Omnijar::GetReader(Omnijar::Type::GRE);
   125             nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get());
   126             NS_ASSERTION(item, "failed to find zip entry");
   128             uint32_t bufSize = item->RealSize();
   129             mFontDataBuf = static_cast<uint8_t*>(moz_malloc(bufSize));
   130             if (mFontDataBuf) {
   131                 nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
   132                 cursor.Copy(&bufSize);
   133                 NS_ASSERTION(bufSize == item->RealSize(),
   134                              "error reading bundled font");
   136                 if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize,
   137                                                     aFontEntry->mFTFontIndex,
   138                                                     &mFace)) {
   139                     NS_WARNING("failed to create freetype face");
   140                 }
   141             }
   142         } else {
   143             if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(),
   144                                          aFontEntry->mFTFontIndex, &mFace)) {
   145                 NS_WARNING("failed to create freetype face");
   146             }
   147         }
   148         if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
   149             NS_WARNING("failed to select Unicode charmap");
   150         }
   151         mOwnsFace = true;
   152     }
   154     ~AutoFTFace() {
   155         if (mFace && mOwnsFace) {
   156             FT_Done_Face(mFace);
   157             if (mFontDataBuf) {
   158                 moz_free(mFontDataBuf);
   159             }
   160         }
   161     }
   163     operator FT_Face() { return mFace; }
   165     // If we 'forget' the FT_Face (used when ownership is handed over to Cairo),
   166     // we do -not- free the mFontDataBuf (if used); that also becomes the
   167     // responsibility of the new owner of the face.
   168     FT_Face forget() {
   169         NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face");
   170         mOwnsFace = false;
   171         return mFace;
   172     }
   174     const uint8_t* FontData() const { return mFontDataBuf; }
   176 private:
   177     FT_Face  mFace;
   178     uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR),
   179                            // or null for fonts instantiated from a file.
   180                            // If non-null, this must survive as long as the
   181                            // FT_Face.
   182     bool     mOwnsFace;
   183 };
   185 /*
   186  * FT2FontEntry
   187  * gfxFontEntry subclass corresponding to a specific face that can be
   188  * rendered by freetype. This is associated with a face index in a
   189  * file (normally a .ttf/.otf file holding a single face, but in principle
   190  * there could be .ttc files with multiple faces).
   191  * The FT2FontEntry can create the necessary FT_Face on demand, and can
   192  * then create a Cairo font_face and scaled_font for drawing.
   193  */
   195 cairo_scaled_font_t *
   196 FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle)
   197 {
   198     cairo_font_face_t *cairoFace = CairoFontFace();
   199     if (!cairoFace) {
   200         return nullptr;
   201     }
   203     cairo_scaled_font_t *scaledFont = nullptr;
   205     cairo_matrix_t sizeMatrix;
   206     cairo_matrix_t identityMatrix;
   208     // XXX deal with adjusted size
   209     cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
   210     cairo_matrix_init_identity(&identityMatrix);
   212     // synthetic oblique by skewing via the font matrix
   213     bool needsOblique = !IsItalic() &&
   214             (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE));
   216     if (needsOblique) {
   217         const double kSkewFactor = 0.25;
   219         cairo_matrix_t style;
   220         cairo_matrix_init(&style,
   221                           1,                //xx
   222                           0,                //yx
   223                           -1 * kSkewFactor,  //xy
   224                           1,                //yy
   225                           0,                //x0
   226                           0);               //y0
   227         cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
   228     }
   230     cairo_font_options_t *fontOptions = cairo_font_options_create();
   232     if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
   233         cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
   234     }
   236     scaledFont = cairo_scaled_font_create(cairoFace,
   237                                           &sizeMatrix,
   238                                           &identityMatrix, fontOptions);
   239     cairo_font_options_destroy(fontOptions);
   241     NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
   242                  "Failed to make scaled font");
   244     return scaledFont;
   245 }
   247 FT2FontEntry::~FT2FontEntry()
   248 {
   249     // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
   250     mFTFace = nullptr;
   252 #ifndef ANDROID
   253     if (mFontFace) {
   254         cairo_font_face_destroy(mFontFace);
   255         mFontFace = nullptr;
   256     }
   257 #endif
   258 }
   260 gfxFont*
   261 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
   262 {
   263     cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
   264     if (!scaledFont) {
   265         return nullptr;
   266     }
   267     gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
   268     cairo_scaled_font_destroy(scaledFont);
   269     return font;
   270 }
   272 /* static */
   273 FT2FontEntry*
   274 FT2FontEntry::CreateFontEntry(const gfxProxyFontEntry &aProxyEntry,
   275                               const uint8_t *aFontData,
   276                               uint32_t aLength)
   277 {
   278     // Ownership of aFontData is passed in here; the fontEntry must
   279     // retain it as long as the FT_Face needs it, and ensure it is
   280     // eventually deleted.
   281     FT_Face face;
   282     FT_Error error =
   283         FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
   284                            aFontData, aLength, 0, &face);
   285     if (error != FT_Err_Ok) {
   286         NS_Free((void*)aFontData);
   287         return nullptr;
   288     }
   289     if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
   290         FT_Done_Face(face);
   291         NS_Free((void*)aFontData);
   292         return nullptr;
   293     }
   294     // Create our FT2FontEntry, which inherits the name of the proxy
   295     // as it's not guaranteed that the face has valid names (bug 737315)
   296     FT2FontEntry* fe =
   297         FT2FontEntry::CreateFontEntry(face, nullptr, 0, aProxyEntry.Name(),
   298                                       aFontData);
   299     if (fe) {
   300         fe->mItalic = aProxyEntry.mItalic;
   301         fe->mWeight = aProxyEntry.mWeight;
   302         fe->mStretch = aProxyEntry.mStretch;
   303         fe->mIsUserFont = true;
   304     }
   305     return fe;
   306 }
   308 class FTUserFontData {
   309 public:
   310     FTUserFontData(FT_Face aFace, const uint8_t* aData)
   311         : mFace(aFace), mFontData(aData)
   312     {
   313     }
   315     ~FTUserFontData()
   316     {
   317         FT_Done_Face(mFace);
   318         if (mFontData) {
   319             NS_Free((void*)mFontData);
   320         }
   321     }
   323     const uint8_t *FontData() const { return mFontData; }
   325 private:
   326     FT_Face        mFace;
   327     const uint8_t *mFontData;
   328 };
   330 static void
   331 FTFontDestroyFunc(void *data)
   332 {
   333     FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
   334     delete userFontData;
   335 }
   337 /* static */
   338 FT2FontEntry*
   339 FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
   340 {
   341     FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
   342     fe->mFilename = aFLE.filepath();
   343     fe->mFTFontIndex = aFLE.index();
   344     fe->mWeight = aFLE.weight();
   345     fe->mStretch = aFLE.stretch();
   346     fe->mItalic = aFLE.italic();
   347     return fe;
   348 }
   350 // Helpers to extract font entry properties from an FT_Face
   351 static bool
   352 FTFaceIsItalic(FT_Face aFace)
   353 {
   354     return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
   355 }
   357 static uint16_t
   358 FTFaceGetWeight(FT_Face aFace)
   359 {
   360     TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
   361     uint16_t os2weight = 0;
   362     if (os2 && os2->version != 0xffff) {
   363         // Technically, only 100 to 900 are valid, but some fonts
   364         // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
   365         // it set to 6 instead of 600.  We try to be nice and handle that
   366         // as well.
   367         if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) {
   368             os2weight = os2->usWeightClass;
   369         } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) {
   370             os2weight = os2->usWeightClass * 100;
   371         }
   372     }
   374     uint16_t result;
   375     if (os2weight != 0) {
   376         result = os2weight;
   377     } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) {
   378         result = 700;
   379     } else {
   380         result = 400;
   381     }
   383     NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!");
   385     return result;
   386 }
   388 // Used to create the font entry for installed faces on the device,
   389 // when iterating over the fonts directories.
   390 // We use the FT_Face to retrieve the details needed for the font entry,
   391 // but unless we have been passed font data (i.e. for a user font),
   392 // we do *not* save a reference to it, nor create a cairo face,
   393 // as we don't want to keep a freetype face for every installed font
   394 // permanently in memory.
   395 /* static */
   396 FT2FontEntry*
   397 FT2FontEntry::CreateFontEntry(FT_Face aFace,
   398                               const char* aFilename, uint8_t aIndex,
   399                               const nsAString& aName,
   400                               const uint8_t *aFontData)
   401 {
   402     FT2FontEntry *fe = new FT2FontEntry(aName);
   403     fe->mItalic = FTFaceIsItalic(aFace);
   404     fe->mWeight = FTFaceGetWeight(aFace);
   405     fe->mFilename = aFilename;
   406     fe->mFTFontIndex = aIndex;
   408     if (aFontData) {
   409         fe->mFTFace = aFace;
   410         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
   411                     FT_LOAD_DEFAULT :
   412                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
   413         fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
   414         FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
   415         cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
   416                                       userFontData, FTFontDestroyFunc);
   417     }
   419     return fe;
   420 }
   422 // construct font entry name for an installed font from names in the FT_Face,
   423 // and then create our FT2FontEntry
   424 static FT2FontEntry*
   425 CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex)
   426 {
   427     if (!aFace->family_name) {
   428         return nullptr;
   429     }
   430     nsAutoString fontName;
   431     AppendUTF8toUTF16(aFace->family_name, fontName);
   432     if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
   433         fontName.AppendLiteral(" ");
   434         AppendUTF8toUTF16(aFace->style_name, fontName);
   435     }
   436     return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName);
   437 }
   439 FT2FontEntry*
   440 gfxFT2Font::GetFontEntry()
   441 {
   442     return static_cast<FT2FontEntry*> (mFontEntry.get());
   443 }
   445 cairo_font_face_t *
   446 FT2FontEntry::CairoFontFace()
   447 {
   448     if (!mFontFace) {
   449         AutoFTFace face(this);
   450         if (!face) {
   451             return nullptr;
   452         }
   453         mFTFace = face.forget();
   454         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
   455                     FT_LOAD_DEFAULT :
   456                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
   457         mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
   458         FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
   459         cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
   460                                       userFontData, FTFontDestroyFunc);
   461     }
   462     return mFontFace;
   463 }
   465 // Copied/modified from similar code in gfxMacPlatformFontList.mm:
   466 // Complex scripts will not render correctly unless Graphite or OT
   467 // layout tables are present.
   468 // For OpenType, we also check that the GSUB table supports the relevant
   469 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
   470 // the characters, but lacks OpenType support).
   472 // TODO: consider whether we should move this to gfxFontEntry and do similar
   473 // cmap-masking on all platforms to avoid using fonts that won't shape
   474 // properly.
   476 nsresult
   477 FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
   478 {
   479     if (mCharacterMap) {
   480         return NS_OK;
   481     }
   483     nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
   485     AutoFallibleTArray<uint8_t,16384> buffer;
   486     nsresult rv = CopyFontTable(TTAG_cmap, buffer);
   488     if (NS_SUCCEEDED(rv)) {
   489         bool unicodeFont;
   490         bool symbolFont;
   491         rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
   492                                     *charmap, mUVSOffset,
   493                                     unicodeFont, symbolFont);
   494     }
   496     if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
   497         // We assume a Graphite font knows what it's doing,
   498         // and provides whatever shaping is needed for the
   499         // characters it supports, so only check/clear the
   500         // complex-script ranges for non-Graphite fonts
   502         // for layout support, check for the presence of opentype layout tables
   503         bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
   505         for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
   506              sr->rangeStart; sr++) {
   507             // check to see if the cmap includes complex script codepoints
   508             if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
   509                 // We check for GSUB here, as GPOS alone would not be ok.
   510                 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
   511                     continue;
   512                 }
   513                 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
   514             }
   515         }
   516     }
   518 #ifdef MOZ_WIDGET_ANDROID
   519     // Hack for the SamsungDevanagari font, bug 1012365:
   520     // pretend the font supports U+0972.
   521     if (!charmap->test(0x0972) &&
   522         charmap->test(0x0905) && charmap->test(0x0945)) {
   523         charmap->set(0x0972);
   524     }
   525 #endif
   527     mHasCmapTable = NS_SUCCEEDED(rv);
   528     if (mHasCmapTable) {
   529         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
   530         mCharacterMap = pfl->FindCharMap(charmap);
   531     } else {
   532         // if error occurred, initialize to null cmap
   533         mCharacterMap = new gfxCharacterMap();
   534     }
   535     return rv;
   536 }
   538 nsresult
   539 FT2FontEntry::CopyFontTable(uint32_t aTableTag,
   540                             FallibleTArray<uint8_t>& aBuffer)
   541 {
   542     AutoFTFace face(this);
   543     if (!face) {
   544         return NS_ERROR_FAILURE;
   545     }
   547     FT_Error status;
   548     FT_ULong len = 0;
   549     status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len);
   550     if (status != FT_Err_Ok || len == 0) {
   551         return NS_ERROR_FAILURE;
   552     }
   554     if (!aBuffer.SetLength(len)) {
   555         return NS_ERROR_OUT_OF_MEMORY;
   556     }
   557     uint8_t *buf = aBuffer.Elements();
   558     status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len);
   559     NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE);
   561     return NS_OK;
   562 }
   564 hb_blob_t*
   565 FT2FontEntry::GetFontTable(uint32_t aTableTag)
   566 {
   567     if (mFontFace) {
   568         // if there's a cairo font face, we may be able to return a blob
   569         // that just wraps a range of the attached user font data
   570         FTUserFontData *userFontData = static_cast<FTUserFontData*>(
   571             cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
   572         if (userFontData && userFontData->FontData()) {
   573             return GetTableFromFontData(userFontData->FontData(), aTableTag);
   574         }
   575     }
   577     // otherwise, use the default method (which in turn will call our
   578     // implementation of CopyFontTable)
   579     return gfxFontEntry::GetFontTable(aTableTag);
   580 }
   582 void
   583 FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
   584                                      FontListSizes* aSizes) const
   585 {
   586     gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   587     aSizes->mFontListSize +=
   588         mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   589 }
   591 void
   592 FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   593                                      FontListSizes* aSizes) const
   594 {
   595     aSizes->mFontListSize += aMallocSizeOf(this);
   596     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   597 }
   599 /*
   600  * FT2FontFamily
   601  * A standard gfxFontFamily; just adds a method used to support sending
   602  * the font list from chrome to content via IPC.
   603  */
   605 void
   606 FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList)
   607 {
   608     for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
   609         const FT2FontEntry *fe =
   610             static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
   611         if (!fe) {
   612             continue;
   613         }
   615         aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
   616                                                fe->mFilename,
   617                                                fe->Weight(), fe->Stretch(),
   618                                                fe->IsItalic(),
   619                                                fe->mFTFontIndex));
   620     }
   621 }
   623 /*
   624  * Startup cache support for the font list:
   625  * We store the list of families and faces, with their style attributes and the
   626  * corresponding font files, in the startup cache.
   627  * This allows us to recreate the gfxFT2FontList collection of families and
   628  * faces without instantiating Freetype faces for each font file (in order to
   629  * find their attributes), leading to significantly quicker startup.
   630  */
   632 #define CACHE_KEY "font.cached-list"
   634 class FontNameCache {
   635 public:
   636     FontNameCache()
   637         : mWriteNeeded(false)
   638     {
   639         mOps = (PLDHashTableOps) {
   640             PL_DHashAllocTable,
   641             PL_DHashFreeTable,
   642             StringHash,
   643             HashMatchEntry,
   644             MoveEntry,
   645             PL_DHashClearEntryStub,
   646             PL_DHashFinalizeStub,
   647             nullptr
   648         };
   650         PL_DHashTableInit(&mMap, &mOps, nullptr, sizeof(FNCMapEntry), 0);
   652         NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default,
   653                           "StartupCacheFontNameCache should only be used in chrome process");
   654         mCache = mozilla::scache::StartupCache::GetSingleton();
   656         Init();
   657     }
   659     ~FontNameCache()
   660     {
   661         if (!mMap.ops) {
   662             return;
   663         }
   664         if (!mWriteNeeded || !mCache) {
   665             PL_DHashTableFinish(&mMap);
   666             return;
   667         }
   669         nsAutoCString buf;
   670         PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf);
   671         PL_DHashTableFinish(&mMap);
   672         mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
   673     }
   675     void Init()
   676     {
   677         if (!mMap.ops || !mCache) {
   678             return;
   679         }
   680         uint32_t size;
   681         char* buf;
   682         if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
   683             return;
   684         }
   686         LOG(("got: %s from the cache", nsDependentCString(buf, size).get()));
   688         const char* beginning = buf;
   689         const char* end = strchr(beginning, ';');
   690         while (end) {
   691             nsCString filename(beginning, end - beginning);
   692             beginning = end + 1;
   693             if (!(end = strchr(beginning, ';'))) {
   694                 break;
   695             }
   696             nsCString faceList(beginning, end - beginning);
   697             beginning = end + 1;
   698             if (!(end = strchr(beginning, ';'))) {
   699                 break;
   700             }
   701             uint32_t timestamp = strtoul(beginning, nullptr, 10);
   702             beginning = end + 1;
   703             if (!(end = strchr(beginning, ';'))) {
   704                 break;
   705             }
   706             uint32_t filesize = strtoul(beginning, nullptr, 10);
   708             FNCMapEntry* mapEntry =
   709                 static_cast<FNCMapEntry*>
   710                 (PL_DHashTableOperate(&mMap, filename.get(), PL_DHASH_ADD));
   711             if (mapEntry) {
   712                 mapEntry->mFilename.Assign(filename);
   713                 mapEntry->mTimestamp = timestamp;
   714                 mapEntry->mFilesize = filesize;
   715                 mapEntry->mFaces.Assign(faceList);
   716                 // entries from the startupcache are marked "non-existing"
   717                 // until we have confirmed that the file still exists
   718                 mapEntry->mFileExists = false;
   719             }
   721             beginning = end + 1;
   722             end = strchr(beginning, ';');
   723         }
   725         // Should we use free() or delete[] here? See bug 684700.
   726         free(buf);
   727     }
   729     virtual void
   730     GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
   731                    uint32_t *aTimestamp, uint32_t *aFilesize)
   732     {
   733         if (!mMap.ops) {
   734             return;
   735         }
   736         PLDHashEntryHdr *hdr =
   737             PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_LOOKUP);
   738         if (!hdr) {
   739             return;
   740         }
   741         FNCMapEntry* entry = static_cast<FNCMapEntry*>(hdr);
   742         if (entry && entry->mFilesize) {
   743             *aTimestamp = entry->mTimestamp;
   744             *aFilesize = entry->mFilesize;
   745             aFaceList.Assign(entry->mFaces);
   746             // this entry does correspond to an existing file
   747             // (although it might not be up-to-date, in which case
   748             // it will get overwritten via CacheFileInfo)
   749             entry->mFileExists = true;
   750         }
   751     }
   753     virtual void
   754     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
   755                   uint32_t aTimestamp, uint32_t aFilesize)
   756     {
   757         if (!mMap.ops) {
   758             return;
   759         }
   760         FNCMapEntry* entry =
   761             static_cast<FNCMapEntry*>
   762             (PL_DHashTableOperate(&mMap, aFileName.get(), PL_DHASH_ADD));
   763         if (entry) {
   764             entry->mFilename.Assign(aFileName);
   765             entry->mTimestamp = aTimestamp;
   766             entry->mFilesize = aFilesize;
   767             entry->mFaces.Assign(aFaceList);
   768             entry->mFileExists = true;
   769         }
   770         mWriteNeeded = true;
   771     }
   773 private:
   774     mozilla::scache::StartupCache* mCache;
   775     PLDHashTable mMap;
   776     bool mWriteNeeded;
   778     PLDHashTableOps mOps;
   780     static PLDHashOperator WriteOutMap(PLDHashTable *aTable,
   781                                        PLDHashEntryHdr *aHdr,
   782                                        uint32_t aNumber, void *aData)
   783     {
   784         FNCMapEntry* entry = static_cast<FNCMapEntry*>(aHdr);
   785         if (!entry->mFileExists) {
   786             // skip writing entries for files that are no longer present
   787             return PL_DHASH_NEXT;
   788         }
   790         nsAutoCString* buf = reinterpret_cast<nsAutoCString*>(aData);
   791         buf->Append(entry->mFilename);
   792         buf->Append(';');
   793         buf->Append(entry->mFaces);
   794         buf->Append(';');
   795         buf->AppendInt(entry->mTimestamp);
   796         buf->Append(';');
   797         buf->AppendInt(entry->mFilesize);
   798         buf->Append(';');
   799         return PL_DHASH_NEXT;
   800     }
   802     typedef struct : public PLDHashEntryHdr {
   803     public:
   804         nsCString mFilename;
   805         uint32_t  mTimestamp;
   806         uint32_t  mFilesize;
   807         nsCString mFaces;
   808         bool      mFileExists;
   809     } FNCMapEntry;
   811     static PLDHashNumber StringHash(PLDHashTable *table, const void *key)
   812     {
   813         return HashString(reinterpret_cast<const char*>(key));
   814     }
   816     static bool HashMatchEntry(PLDHashTable *table,
   817                                  const PLDHashEntryHdr *aHdr, const void *key)
   818     {
   819         const FNCMapEntry* entry =
   820             static_cast<const FNCMapEntry*>(aHdr);
   821         return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
   822     }
   824     static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
   825                           PLDHashEntryHdr *aTo)
   826     {
   827         FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
   828         const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
   829         to->mFilename.Assign(from->mFilename);
   830         to->mTimestamp = from->mTimestamp;
   831         to->mFilesize = from->mFilesize;
   832         to->mFaces.Assign(from->mFaces);
   833         to->mFileExists = from->mFileExists;
   834     }
   835 };
   837 /***************************************************************
   838  *
   839  * gfxFT2FontList
   840  *
   841  */
   843 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
   844 // scanning the system's Fonts directory for OpenType and TrueType files.
   846 gfxFT2FontList::gfxFT2FontList()
   847 {
   848 }
   850 void
   851 gfxFT2FontList::AppendFacesFromCachedFaceList(const nsCString& aFileName,
   852                                               bool aStdFile,
   853                                               const nsCString& aFaceList)
   854 {
   855     const char *beginning = aFaceList.get();
   856     const char *end = strchr(beginning, ',');
   857     while (end) {
   858         nsString familyName =
   859             NS_ConvertUTF8toUTF16(beginning, end - beginning);
   860         ToLowerCase(familyName);
   861         beginning = end + 1;
   862         if (!(end = strchr(beginning, ','))) {
   863             break;
   864         }
   865         nsString faceName =
   866             NS_ConvertUTF8toUTF16(beginning, end - beginning);
   867         beginning = end + 1;
   868         if (!(end = strchr(beginning, ','))) {
   869             break;
   870         }
   871         uint32_t index = strtoul(beginning, nullptr, 10);
   872         beginning = end + 1;
   873         if (!(end = strchr(beginning, ','))) {
   874             break;
   875         }
   876         bool italic = (*beginning != '0');
   877         beginning = end + 1;
   878         if (!(end = strchr(beginning, ','))) {
   879             break;
   880         }
   881         uint32_t weight = strtoul(beginning, nullptr, 10);
   882         beginning = end + 1;
   883         if (!(end = strchr(beginning, ','))) {
   884             break;
   885         }
   886         int32_t stretch = strtol(beginning, nullptr, 10);
   888         FontListEntry fle(familyName, faceName, aFileName,
   889                           weight, stretch, italic, index);
   890         AppendFaceFromFontListEntry(fle, aStdFile);
   892         beginning = end + 1;
   893         end = strchr(beginning, ',');
   894     }
   895 }
   897 static void
   898 AppendToFaceList(nsCString& aFaceList,
   899                  nsAString& aFamilyName, FT2FontEntry* aFontEntry)
   900 {
   901     aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
   902     aFaceList.Append(',');
   903     aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
   904     aFaceList.Append(',');
   905     aFaceList.AppendInt(aFontEntry->mFTFontIndex);
   906     aFaceList.Append(',');
   907     aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
   908     aFaceList.Append(',');
   909     aFaceList.AppendInt(aFontEntry->Weight());
   910     aFaceList.Append(',');
   911     aFaceList.AppendInt(aFontEntry->Stretch());
   912     aFaceList.Append(',');
   913 }
   915 void
   916 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
   917 {
   918     // note if the family is in the "bad underline" blacklist
   919     if (aFamily->IsBadUnderlineFamily()) {
   920         mIsBadUnderlineFont = true;
   921     }
   923     // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
   924     // because of unwanted on-by-default "ae" ligature.
   925     // (See also AppendFaceFromFontListEntry.)
   926     if (aFamily->Name().EqualsLiteral("roboto")) {
   927         mIgnoreGSUB = true;
   928     }
   930     // bug 706888 - set the IgnoreGSUB flag on the broken version of
   931     // Droid Sans Arabic from certain phones, as identified by the
   932     // font checksum in the 'head' table
   933     else if (aFamily->Name().EqualsLiteral("droid sans arabic")) {
   934         AutoFTFace face(this);
   935         if (face) {
   936             const TT_Header *head = static_cast<const TT_Header*>
   937                 (FT_Get_Sfnt_Table(face, ft_sfnt_head));
   938             if (head && head->CheckSum_Adjust == 0xe445242) {
   939                 mIgnoreGSUB = true;
   940             }
   941         }
   942     }
   943 }
   945 void
   946 gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
   947                                         bool aStdFile,
   948                                         FontNameCache *aCache)
   949 {
   950     nsCString faceList;
   951     uint32_t filesize = 0, timestamp = 0;
   952     if (aCache) {
   953         aCache->GetInfoForFile(aFileName, faceList, &timestamp, &filesize);
   954     }
   956     struct stat s;
   957     int statRetval = stat(aFileName.get(), &s);
   958     if (!faceList.IsEmpty() && 0 == statRetval &&
   959         s.st_mtime == timestamp && s.st_size == filesize)
   960     {
   961         LOG(("using cached font info for %s", aFileName.get()));
   962         AppendFacesFromCachedFaceList(aFileName, aStdFile, faceList);
   963         return;
   964     }
   966 #ifdef XP_WIN
   967     FT_Library ftLibrary = gfxWindowsPlatform::GetPlatform()->GetFTLibrary();
   968 #elif defined(ANDROID)
   969     FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
   970 #endif
   971     FT_Face dummy;
   972     if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
   973         LOG(("reading font info via FreeType for %s", aFileName.get()));
   974         nsCString faceList;
   975         timestamp = s.st_mtime;
   976         filesize = s.st_size;
   977         for (FT_Long i = 0; i < dummy->num_faces; i++) {
   978             FT_Face face;
   979             if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
   980                 continue;
   981             }
   982             AddFaceToList(aFileName, i, aStdFile, face, faceList);
   983             FT_Done_Face(face);
   984         }
   985         FT_Done_Face(dummy);
   986         if (aCache && 0 == statRetval && !faceList.IsEmpty()) {
   987             aCache->CacheFileInfo(aFileName, faceList, timestamp, filesize);
   988         }
   989     }
   990 }
   992 #define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
   994 void
   995 gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
   996 {
   997     bool jarChanged = false;
   999     mozilla::scache::StartupCache* cache =
  1000         mozilla::scache::StartupCache::GetSingleton();
  1001     char *cachedModifiedTimeBuf;
  1002     uint32_t longSize;
  1003     int64_t jarModifiedTime;
  1004     if (cache &&
  1005         NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
  1006                                       &cachedModifiedTimeBuf,
  1007                                       &longSize)) &&
  1008         longSize == sizeof(int64_t))
  1010         nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
  1011         jarFile->GetLastModifiedTime(&jarModifiedTime);
  1012         if (jarModifiedTime > *(int64_t*)cachedModifiedTimeBuf) {
  1013             jarChanged = true;
  1017     static const char* sJarSearchPaths[] = {
  1018         "res/fonts/*.ttf$",
  1019     };
  1020     nsRefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
  1021     for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) {
  1022         nsZipFind* find;
  1023         if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
  1024             const char* path;
  1025             uint16_t len;
  1026             while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
  1027                 nsCString entryName(path, len);
  1028                 AppendFacesFromOmnijarEntry(reader, entryName, aCache,
  1029                                             jarChanged);
  1031             delete find;
  1035     if (cache) {
  1036         cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime,
  1037                          sizeof(jarModifiedTime));
  1041 // Given the freetype face corresponding to an entryName and face index,
  1042 // add the face to the available font list and to the faceList string
  1043 void
  1044 gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
  1045                               bool aStdFile, FT_Face aFace,
  1046                               nsCString& aFaceList)
  1048     if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
  1049         // ignore faces that don't support a Unicode charmap
  1050         return;
  1053     // build the font entry name and create an FT2FontEntry,
  1054     // but do -not- keep a reference to the FT_Face
  1055     FT2FontEntry* fe =
  1056         CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
  1058     if (fe) {
  1059         NS_ConvertUTF8toUTF16 name(aFace->family_name);
  1060         BuildKeyNameFromFontName(name);
  1061         gfxFontFamily *family = mFontFamilies.GetWeak(name);
  1062         if (!family) {
  1063             family = new FT2FontFamily(name);
  1064             mFontFamilies.Put(name, family);
  1065             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
  1066                 family->SetSkipSpaceFeatureCheck(true);
  1068             if (mBadUnderlineFamilyNames.Contains(name)) {
  1069                 family->SetBadUnderlineFamily();
  1072         fe->mStandardFace = aStdFile;
  1073         family->AddFontEntry(fe);
  1075         fe->CheckForBrokenFont(family);
  1077         AppendToFaceList(aFaceList, name, fe);
  1078 #ifdef PR_LOGGING
  1079         if (LOG_ENABLED()) {
  1080             LOG(("(fontinit) added (%s) to family (%s)"
  1081                  " with style: %s weight: %d stretch: %d",
  1082                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
  1083                  NS_ConvertUTF16toUTF8(family->Name()).get(),
  1084                  fe->IsItalic() ? "italic" : "normal",
  1085                  fe->Weight(), fe->Stretch()));
  1087 #endif
  1091 void
  1092 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
  1093                                             const nsCString& aEntryName,
  1094                                             FontNameCache *aCache,
  1095                                             bool aJarChanged)
  1097     nsCString faceList;
  1098     if (aCache && !aJarChanged) {
  1099         uint32_t filesize, timestamp;
  1100         aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
  1101         if (faceList.Length() > 0) {
  1102             AppendFacesFromCachedFaceList(aEntryName, true, faceList);
  1103             return;
  1107     nsZipItem *item = aArchive->GetItem(aEntryName.get());
  1108     NS_ASSERTION(item, "failed to find zip entry");
  1110     uint32_t bufSize = item->RealSize();
  1111     // We use fallible allocation here; if there's not enough RAM, we'll simply
  1112     // ignore the bundled fonts and fall back to the device's installed fonts.
  1113     nsAutoPtr<uint8_t> buf(static_cast<uint8_t*>(moz_malloc(bufSize)));
  1114     if (!buf) {
  1115         return;
  1118     nsZipCursor cursor(item, aArchive, buf, bufSize);
  1119     uint8_t* data = cursor.Copy(&bufSize);
  1120     NS_ASSERTION(data && bufSize == item->RealSize(),
  1121                  "error reading bundled font");
  1122     if (!data) {
  1123         return;
  1126     FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
  1128     FT_Face dummy;
  1129     if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, 0, &dummy)) {
  1130         return;
  1133     for (FT_Long i = 0; i < dummy->num_faces; i++) {
  1134         FT_Face face;
  1135         if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf, bufSize, i, &face)) {
  1136             continue;
  1138         AddFaceToList(aEntryName, i, true, face, faceList);
  1139         FT_Done_Face(face);
  1142     FT_Done_Face(dummy);
  1144     if (aCache && !faceList.IsEmpty()) {
  1145         aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
  1149 // Called on each family after all fonts are added to the list;
  1150 // this will sort faces to give priority to "standard" font files
  1151 // if aUserArg is non-null (i.e. we're using it as a boolean flag)
  1152 static PLDHashOperator
  1153 FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
  1154                          nsRefPtr<gfxFontFamily>& aFamily,
  1155                          void* aUserArg)
  1157     gfxFontFamily *family = aFamily.get();
  1158     bool sortFaces = (aUserArg != nullptr);
  1160     family->SetHasStyles(true);
  1162     if (sortFaces) {
  1163         family->SortAvailableFonts();
  1165     family->CheckForSimpleFamily();
  1167     return PL_DHASH_NEXT;
  1170 void
  1171 gfxFT2FontList::FindFonts()
  1173 #ifdef XP_WIN
  1174     nsTArray<nsString> searchPaths(3);
  1175     nsTArray<nsString> fontPatterns(3);
  1176     fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttf"));
  1177     fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.ttc"));
  1178     fontPatterns.AppendElement(NS_LITERAL_STRING("\\*.otf"));
  1179     wchar_t pathBuf[256];
  1180     SHGetSpecialFolderPathW(0, pathBuf, CSIDL_WINDOWS, 0);
  1181     searchPaths.AppendElement(pathBuf);
  1182     SHGetSpecialFolderPathW(0, pathBuf, CSIDL_FONTS, 0);
  1183     searchPaths.AppendElement(pathBuf);
  1184     nsCOMPtr<nsIFile> resDir;
  1185     NS_GetSpecialDirectory(NS_APP_RES_DIR, getter_AddRefs(resDir));
  1186     if (resDir) {
  1187         resDir->Append(NS_LITERAL_STRING("fonts"));
  1188         nsAutoString resPath;
  1189         resDir->GetPath(resPath);
  1190         searchPaths.AppendElement(resPath);
  1192     WIN32_FIND_DATAW results;
  1193     for (uint32_t i = 0;  i < searchPaths.Length(); i++) {
  1194         const nsString& path(searchPaths[i]);
  1195         for (uint32_t j = 0; j < fontPatterns.Length(); j++) { 
  1196             nsAutoString pattern(path);
  1197             pattern.Append(fontPatterns[j]);
  1198             HANDLE handle = FindFirstFileExW(pattern.get(),
  1199                                              FindExInfoStandard,
  1200                                              &results,
  1201                                              FindExSearchNameMatch,
  1202                                              nullptr,
  1203                                              0);
  1204             bool moreFiles = handle != INVALID_HANDLE_VALUE;
  1205             while (moreFiles) {
  1206                 nsAutoString filePath(path);
  1207                 filePath.AppendLiteral("\\");
  1208                 filePath.Append(results.cFileName);
  1209                 AppendFacesFromFontFile(NS_ConvertUTF16toUTF8(filePath));
  1210                 moreFiles = FindNextFile(handle, &results);
  1212             if (handle != INVALID_HANDLE_VALUE)
  1213                 FindClose(handle);
  1216 #elif defined(ANDROID)
  1217     gfxFontCache *fc = gfxFontCache::GetCache();
  1218     if (fc)
  1219         fc->AgeAllGenerations();
  1220     mPrefFonts.Clear();
  1221     mCodepointsWithNoFonts.reset();
  1223     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
  1224     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
  1226     if (XRE_GetProcessType() != GeckoProcessType_Default) {
  1227         // Content process: ask the Chrome process to give us the list
  1228         InfallibleTArray<FontListEntry> fonts;
  1229         mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
  1230         for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
  1231             AppendFaceFromFontListEntry(fonts[i], false);
  1233         // Passing null for userdata tells Finalize that it does not need
  1234         // to sort faces (because they were already sorted by chrome,
  1235         // so we just maintain the existing order)
  1236         mFontFamilies.Enumerate(FinalizeFamilyMemberList, nullptr);
  1237         LOG(("got font list from chrome process: %d faces in %d families",
  1238             fonts.Length(), mFontFamilies.Count()));
  1239         return;
  1242     // Chrome process: get the cached list (if any)
  1243     FontNameCache fnc;
  1245     // ANDROID_ROOT is the root of the android system, typically /system;
  1246     // font files are in /$ANDROID_ROOT/fonts/
  1247     nsCString root;
  1248     char *androidRoot = PR_GetEnv("ANDROID_ROOT");
  1249     if (androidRoot) {
  1250         root = androidRoot;
  1251     } else {
  1252         root = NS_LITERAL_CSTRING("/system");
  1254     root.Append("/fonts");
  1256     FindFontsInDir(root, &fnc);
  1258     if (mFontFamilies.Count() == 0) {
  1259         // if we can't find/read the font directory, we are doomed!
  1260         NS_RUNTIMEABORT("Could not read the system fonts directory");
  1262 #endif // XP_WIN && ANDROID
  1264     // Look for fonts stored in omnijar, unless we're on a low-memory
  1265     // device where we don't want to spend the RAM to decompress them.
  1266     // (Prefs may disable this, or force-enable it even with low memory.)
  1267     bool lowmem;
  1268     nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
  1269     if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
  1270          Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
  1271         Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
  1272         FindFontsInOmnijar(&fnc);
  1275     // look for locally-added fonts in a "fonts" subdir of the profile
  1276     nsCOMPtr<nsIFile> localDir;
  1277     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
  1278                                          getter_AddRefs(localDir));
  1279     if (NS_SUCCEEDED(rv) &&
  1280         NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
  1281         nsCString localPath;
  1282         rv = localDir->GetNativePath(localPath);
  1283         if (NS_SUCCEEDED(rv)) {
  1284             FindFontsInDir(localPath, &fnc);
  1288     // Finalize the families by sorting faces into standard order
  1289     // and marking "simple" families.
  1290     // Passing non-null userData here says that we want faces to be sorted.
  1291     mFontFamilies.Enumerate(FinalizeFamilyMemberList, this);
  1294 #ifdef ANDROID
  1295 void
  1296 gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
  1298     static const char* sStandardFonts[] = {
  1299         "DroidSans.ttf",
  1300         "DroidSans-Bold.ttf",
  1301         "DroidSerif-Regular.ttf",
  1302         "DroidSerif-Bold.ttf",
  1303         "DroidSerif-Italic.ttf",
  1304         "DroidSerif-BoldItalic.ttf",
  1305         "DroidSansMono.ttf",
  1306         "DroidSansArabic.ttf",
  1307         "DroidSansHebrew.ttf",
  1308         "DroidSansThai.ttf",
  1309         "MTLmr3m.ttf",
  1310         "MTLc3m.ttf",
  1311         "NanumGothic.ttf",
  1312         "DroidSansJapanese.ttf",
  1313         "DroidSansFallback.ttf"
  1314     };
  1316     DIR *d = opendir(aDir.get());
  1317     if (!d) {
  1318         return;
  1321     struct dirent *ent = nullptr;
  1322     while ((ent = readdir(d)) != nullptr) {
  1323         const char *ext = strrchr(ent->d_name, '.');
  1324         if (!ext) {
  1325             continue;
  1327         if (strcasecmp(ext, ".ttf") == 0 ||
  1328             strcasecmp(ext, ".otf") == 0 ||
  1329             strcasecmp(ext, ".woff") == 0 ||
  1330             strcasecmp(ext, ".ttc") == 0) {
  1331             bool isStdFont = false;
  1332             for (unsigned int i = 0;
  1333                  i < ArrayLength(sStandardFonts) && !isStdFont; i++) {
  1334                 isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
  1337             nsCString s(aDir);
  1338             s.Append('/');
  1339             s.Append(ent->d_name);
  1341             // Add the face(s) from this file to our font list;
  1342             // note that if we have cached info for this file in fnc,
  1343             // and the file is unchanged, we won't actually need to read it.
  1344             // If the file is new/changed, this will update the FontNameCache.
  1345             AppendFacesFromFontFile(s, isStdFont, aFNC);
  1349     closedir(d);
  1351 #endif
  1353 void
  1354 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
  1355                                             bool aStdFile)
  1357     FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
  1358     if (fe) {
  1359         fe->mStandardFace = aStdFile;
  1360         nsAutoString name(aFLE.familyName());
  1361         gfxFontFamily *family = mFontFamilies.GetWeak(name);
  1362         if (!family) {
  1363             family = new FT2FontFamily(name);
  1364             mFontFamilies.Put(name, family);
  1365             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
  1366                 family->SetSkipSpaceFeatureCheck(true);
  1368             if (mBadUnderlineFamilyNames.Contains(name)) {
  1369                 family->SetBadUnderlineFamily();
  1372         family->AddFontEntry(fe);
  1374         fe->CheckForBrokenFont(family);
  1378 static PLDHashOperator
  1379 AddFamilyToFontList(nsStringHashKey::KeyType aKey,
  1380                     nsRefPtr<gfxFontFamily>& aFamily,
  1381                     void* aUserArg)
  1383     InfallibleTArray<FontListEntry>* fontlist =
  1384         reinterpret_cast<InfallibleTArray<FontListEntry>*>(aUserArg);
  1386     FT2FontFamily *family = static_cast<FT2FontFamily*>(aFamily.get());
  1387     family->AddFacesToFontList(fontlist);
  1389     return PL_DHASH_NEXT;
  1392 void
  1393 gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue)
  1395     mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
  1398 static void
  1399 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
  1401     nsAutoTArray<nsString, 5> skiplist;
  1402     gfxFontUtils::GetPrefsFontList(
  1403         "font.whitelist.skip_default_features_space_check",
  1404         skiplist);
  1405     uint32_t numFonts = skiplist.Length();
  1406     for (uint32_t i = 0; i < numFonts; i++) {
  1407         ToLowerCase(skiplist[i]);
  1408         aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
  1412 nsresult
  1413 gfxFT2FontList::InitFontList()
  1415     // reset font lists
  1416     gfxPlatformFontList::InitFontList();
  1418     LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
  1420     FindFonts();
  1422     return NS_OK;
  1425 struct FullFontNameSearch {
  1426     FullFontNameSearch(const nsAString& aFullName)
  1427         : mFullName(aFullName), mFontEntry(nullptr)
  1428     { }
  1430     nsString     mFullName;
  1431     FT2FontEntry *mFontEntry;
  1432 };
  1434 // callback called for each family name, based on the assumption that the 
  1435 // first part of the full name is the family name
  1436 static PLDHashOperator
  1437 FindFullName(nsStringHashKey::KeyType aKey,
  1438              nsRefPtr<gfxFontFamily>& aFontFamily,
  1439              void* userArg)
  1441     FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg);
  1443     // does the family name match up to the length of the family name?
  1444     const nsString& family = aFontFamily->Name();
  1446     nsString fullNameFamily;
  1447     data->mFullName.Left(fullNameFamily, family.Length());
  1449     // if so, iterate over faces in this family to see if there is a match
  1450     if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
  1451         nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList();
  1452         int index, len = fontList.Length();
  1453         for (index = 0; index < len; index++) {
  1454             gfxFontEntry* fe = fontList[index];
  1455             if (!fe) {
  1456                 continue;
  1458             if (fe->Name().Equals(data->mFullName,
  1459                                   nsCaseInsensitiveStringComparator())) {
  1460                 data->mFontEntry = static_cast<FT2FontEntry*>(fe);
  1461                 return PL_DHASH_STOP;
  1466     return PL_DHASH_NEXT;
  1469 gfxFontEntry* 
  1470 gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
  1471                                 const nsAString& aFontName)
  1473     // walk over list of names
  1474     FullFontNameSearch data(aFontName);
  1476     mFontFamilies.Enumerate(FindFullName, &data);
  1478     if (!data.mFontEntry) {
  1479         return nullptr;
  1482     // Clone the font entry so that we can then set its style descriptors
  1483     // from the proxy rather than the actual font.
  1485     // Ensure existence of mFTFace in the original entry
  1486     data.mFontEntry->CairoFontFace();
  1487     if (!data.mFontEntry->mFTFace) {
  1488         return nullptr;
  1491     FT2FontEntry* fe =
  1492         FT2FontEntry::CreateFontEntry(data.mFontEntry->mFTFace,
  1493                                       data.mFontEntry->mFilename.get(),
  1494                                       data.mFontEntry->mFTFontIndex,
  1495                                       data.mFontEntry->Name(), nullptr);
  1496     if (fe) {
  1497         fe->mItalic = aProxyEntry->mItalic;
  1498         fe->mWeight = aProxyEntry->mWeight;
  1499         fe->mStretch = aProxyEntry->mStretch;
  1500         fe->mIsUserFont = fe->mIsLocalUserFont = true;
  1503     return fe;
  1506 gfxFontFamily*
  1507 gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle)
  1509 #ifdef XP_WIN
  1510     HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT);
  1511     LOGFONTW logFont;
  1512     if (hGDI && ::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
  1513         nsAutoString resolvedName;
  1514         if (ResolveFontName(nsDependentString(logFont.lfFaceName), resolvedName)) {
  1515             return FindFamily(resolvedName);
  1518 #elif defined(MOZ_WIDGET_GONK)
  1519     nsAutoString resolvedName;
  1520     if (ResolveFontName(NS_LITERAL_STRING("Fira Sans OT"), resolvedName)) {
  1521         return FindFamily(resolvedName);
  1523 #elif defined(MOZ_WIDGET_ANDROID)
  1524     nsAutoString resolvedName;
  1525     if (ResolveFontName(NS_LITERAL_STRING("Roboto"), resolvedName) ||
  1526         ResolveFontName(NS_LITERAL_STRING("Droid Sans"), resolvedName)) {
  1527         return FindFamily(resolvedName);
  1529 #endif
  1530     /* TODO: what about Qt or other platforms that may use this? */
  1531     return nullptr;
  1534 gfxFontEntry*
  1535 gfxFT2FontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
  1536                                  const uint8_t *aFontData,
  1537                                  uint32_t aLength)
  1539     // The FT2 font needs the font data to persist, so we do NOT free it here
  1540     // but instead pass ownership to the font entry.
  1541     // Deallocation will happen later, when the font face is destroyed.
  1542     return FT2FontEntry::CreateFontEntry(*aProxyEntry, aFontData, aLength);

mercurial