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/. */
5 
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/Base64.h"
8 #include "mozilla/MemoryReporting.h"
9 
10 #include "mozilla/dom/ContentChild.h"
11 #include "gfxAndroidPlatform.h"
12 #include "mozilla/Omnijar.h"
13 #include "mozilla/UniquePtr.h"
14 #include "mozilla/UniquePtrExtensions.h"
15 #include "nsIInputStream.h"
16 #define gfxToolkitPlatform gfxAndroidPlatform
17 
18 #include "nsXULAppAPI.h"
19 #include <dirent.h>
20 #include <android/log.h>
21 #define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
22 
23 #include "ft2build.h"
24 #include FT_FREETYPE_H
25 #include FT_TRUETYPE_TAGS_H
26 #include FT_TRUETYPE_TABLES_H
27 #include "cairo-ft.h"
28 
29 #include "gfxFT2FontList.h"
30 #include "gfxFT2Fonts.h"
31 #include "gfxUserFontSet.h"
32 #include "gfxFontUtils.h"
33 
34 #include "nsServiceManagerUtils.h"
35 #include "nsIObserverService.h"
36 #include "nsTArray.h"
37 #include "nsUnicharUtils.h"
38 #include "nsCRT.h"
39 
40 #include "nsDirectoryServiceUtils.h"
41 #include "nsDirectoryServiceDefs.h"
42 #include "nsAppDirectoryServiceDefs.h"
43 #include "nsISimpleEnumerator.h"
44 #include "nsIMemory.h"
45 #include "gfxFontConstants.h"
46 
47 #include "mozilla/Preferences.h"
48 #include "mozilla/scache/StartupCache.h"
49 #include <fcntl.h>
50 #include <sys/mman.h>
51 #include <sys/stat.h>
52 
53 using namespace mozilla;
54 
55 static LazyLogModule sFontInfoLog("fontInfoLog");
56 
57 #undef LOG
58 #define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args)
59 #define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug)
60 
61 static cairo_user_data_key_t sFTUserFontDataKey;
62 
63 static __inline void
BuildKeyNameFromFontName(nsAString & aName)64 BuildKeyNameFromFontName(nsAString &aName)
65 {
66     ToLowerCase(aName);
67 }
68 
69 // Helper to access the FT_Face for a given FT2FontEntry,
70 // creating a temporary face if the entry does not have one yet.
71 // This allows us to read font names, tables, etc if necessary
72 // without permanently instantiating a freetype face and consuming
73 // memory long-term.
74 // This may fail (resulting in a null FT_Face), e.g. if it fails to
75 // allocate memory to uncompress a font from omnijar.
76 class AutoFTFace {
77 public:
AutoFTFace(FT2FontEntry * aFontEntry)78     AutoFTFace(FT2FontEntry* aFontEntry)
79         : mFace(nullptr), mFontDataBuf(nullptr), mOwnsFace(false)
80     {
81         if (aFontEntry->mFTFace) {
82             mFace = aFontEntry->mFTFace;
83             return;
84         }
85 
86         NS_ASSERTION(!aFontEntry->mFilename.IsEmpty(),
87                      "can't use AutoFTFace for fonts without a filename");
88         FT_Library ft = gfxToolkitPlatform::GetPlatform()->GetFTLibrary();
89 
90         // A relative path (no initial "/") means this is a resource in
91         // omnijar, not an installed font on the device.
92         // The NS_ASSERTIONs here should never fail, as the resource must have
93         // been read successfully during font-list initialization or we'd never
94         // have created the font entry. The only legitimate runtime failure
95         // here would be memory allocation, in which case mFace remains null.
96         if (aFontEntry->mFilename[0] != '/') {
97             RefPtr<nsZipArchive> reader =
98                 Omnijar::GetReader(Omnijar::Type::GRE);
99             nsZipItem *item = reader->GetItem(aFontEntry->mFilename.get());
100             NS_ASSERTION(item, "failed to find zip entry");
101 
102             uint32_t bufSize = item->RealSize();
103             mFontDataBuf = static_cast<uint8_t*>(malloc(bufSize));
104             if (mFontDataBuf) {
105                 nsZipCursor cursor(item, reader, mFontDataBuf, bufSize);
106                 cursor.Copy(&bufSize);
107                 NS_ASSERTION(bufSize == item->RealSize(),
108                              "error reading bundled font");
109 
110                 if (FT_Err_Ok != FT_New_Memory_Face(ft, mFontDataBuf, bufSize,
111                                                     aFontEntry->mFTFontIndex,
112                                                     &mFace)) {
113                     NS_WARNING("failed to create freetype face");
114                 }
115             }
116         } else {
117             if (FT_Err_Ok != FT_New_Face(ft, aFontEntry->mFilename.get(),
118                                          aFontEntry->mFTFontIndex, &mFace)) {
119                 NS_WARNING("failed to create freetype face");
120             }
121         }
122         if (FT_Err_Ok != FT_Select_Charmap(mFace, FT_ENCODING_UNICODE)) {
123             NS_WARNING("failed to select Unicode charmap");
124         }
125         mOwnsFace = true;
126     }
127 
~AutoFTFace()128     ~AutoFTFace() {
129         if (mFace && mOwnsFace) {
130             FT_Done_Face(mFace);
131             if (mFontDataBuf) {
132                 free(mFontDataBuf);
133             }
134         }
135     }
136 
operator FT_Face()137     operator FT_Face() { return mFace; }
138 
139     // If we 'forget' the FT_Face (used when ownership is handed over to Cairo),
140     // we do -not- free the mFontDataBuf (if used); that also becomes the
141     // responsibility of the new owner of the face.
forget()142     FT_Face forget() {
143         NS_ASSERTION(mOwnsFace, "can't forget() when we didn't own the face");
144         mOwnsFace = false;
145         return mFace;
146     }
147 
FontData() const148     const uint8_t* FontData() const { return mFontDataBuf; }
149 
150 private:
151     FT_Face  mFace;
152     uint8_t* mFontDataBuf; // Uncompressed data (for fonts stored in a JAR),
153                            // or null for fonts instantiated from a file.
154                            // If non-null, this must survive as long as the
155                            // FT_Face.
156     bool     mOwnsFace;
157 };
158 
159 /*
160  * FT2FontEntry
161  * gfxFontEntry subclass corresponding to a specific face that can be
162  * rendered by freetype. This is associated with a face index in a
163  * file (normally a .ttf/.otf file holding a single face, but in principle
164  * there could be .ttc files with multiple faces).
165  * The FT2FontEntry can create the necessary FT_Face on demand, and can
166  * then create a Cairo font_face and scaled_font for drawing.
167  */
168 
169 cairo_scaled_font_t *
CreateScaledFont(const gfxFontStyle * aStyle)170 FT2FontEntry::CreateScaledFont(const gfxFontStyle *aStyle)
171 {
172     cairo_font_face_t *cairoFace = CairoFontFace();
173     if (!cairoFace) {
174         return nullptr;
175     }
176 
177     cairo_scaled_font_t *scaledFont = nullptr;
178 
179     cairo_matrix_t sizeMatrix;
180     cairo_matrix_t identityMatrix;
181 
182     // XXX deal with adjusted size
183     cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
184     cairo_matrix_init_identity(&identityMatrix);
185 
186     // synthetic oblique by skewing via the font matrix
187     bool needsOblique = IsUpright() &&
188                         aStyle->style != NS_FONT_STYLE_NORMAL &&
189                         aStyle->allowSyntheticStyle;
190 
191     if (needsOblique) {
192         cairo_matrix_t style;
193         cairo_matrix_init(&style,
194                           1,                //xx
195                           0,                //yx
196                           -1 * OBLIQUE_SKEW_FACTOR, //xy
197                           1,                //yy
198                           0,                //x0
199                           0);               //y0
200         cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
201     }
202 
203     cairo_font_options_t *fontOptions = cairo_font_options_create();
204 
205     if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
206         cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
207     }
208 
209     scaledFont = cairo_scaled_font_create(cairoFace,
210                                           &sizeMatrix,
211                                           &identityMatrix, fontOptions);
212     cairo_font_options_destroy(fontOptions);
213 
214     NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
215                  "Failed to make scaled font");
216 
217     return scaledFont;
218 }
219 
~FT2FontEntry()220 FT2FontEntry::~FT2FontEntry()
221 {
222     // Do nothing for mFTFace here since FTFontDestroyFunc is called by cairo.
223     mFTFace = nullptr;
224 
225 #ifndef ANDROID
226     if (mFontFace) {
227         cairo_font_face_destroy(mFontFace);
228         mFontFace = nullptr;
229     }
230 #endif
231 }
232 
233 gfxFont*
CreateFontInstance(const gfxFontStyle * aFontStyle,bool aNeedsBold)234 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
235 {
236     cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
237     if (!scaledFont) {
238         return nullptr;
239     }
240     gfxFont *font = new gfxFT2Font(scaledFont, this, aFontStyle, aNeedsBold);
241     cairo_scaled_font_destroy(scaledFont);
242     return font;
243 }
244 
245 /* static */
246 FT2FontEntry*
CreateFontEntry(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle,const uint8_t * aFontData,uint32_t aLength)247 FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
248                               uint16_t aWeight,
249                               int16_t aStretch,
250                               uint8_t aStyle,
251                               const uint8_t* aFontData,
252                               uint32_t aLength)
253 {
254     // Ownership of aFontData is passed in here; the fontEntry must
255     // retain it as long as the FT_Face needs it, and ensure it is
256     // eventually deleted.
257     FT_Face face;
258     FT_Error error =
259         FT_New_Memory_Face(gfxToolkitPlatform::GetPlatform()->GetFTLibrary(),
260                            aFontData, aLength, 0, &face);
261     if (error != FT_Err_Ok) {
262         free((void*)aFontData);
263         return nullptr;
264     }
265     if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
266         FT_Done_Face(face);
267         free((void*)aFontData);
268         return nullptr;
269     }
270     // Create our FT2FontEntry, which inherits the name of the userfont entry
271     // as it's not guaranteed that the face has valid names (bug 737315)
272     FT2FontEntry* fe =
273         FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
274                                       aFontData);
275     if (fe) {
276         fe->mStyle = aStyle;
277         fe->mWeight = aWeight;
278         fe->mStretch = aStretch;
279         fe->mIsDataUserFont = true;
280     }
281     return fe;
282 }
283 
284 class FTUserFontData {
285 public:
FTUserFontData(FT_Face aFace,const uint8_t * aData)286     FTUserFontData(FT_Face aFace, const uint8_t* aData)
287         : mFace(aFace), mFontData(aData)
288     {
289     }
290 
~FTUserFontData()291     ~FTUserFontData()
292     {
293         FT_Done_Face(mFace);
294         if (mFontData) {
295             free((void*)mFontData);
296         }
297     }
298 
FontData() const299     const uint8_t *FontData() const { return mFontData; }
300 
301 private:
302     FT_Face        mFace;
303     const uint8_t *mFontData;
304 };
305 
306 static void
FTFontDestroyFunc(void * data)307 FTFontDestroyFunc(void *data)
308 {
309     FTUserFontData *userFontData = static_cast<FTUserFontData*>(data);
310     delete userFontData;
311 }
312 
313 /* static */
314 FT2FontEntry*
CreateFontEntry(const FontListEntry & aFLE)315 FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
316 {
317     FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
318     fe->mFilename = aFLE.filepath();
319     fe->mFTFontIndex = aFLE.index();
320     fe->mWeight = aFLE.weight();
321     fe->mStretch = aFLE.stretch();
322     fe->mStyle = (aFLE.italic() ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
323     return fe;
324 }
325 
326 // Helpers to extract font entry properties from an FT_Face
327 static bool
FTFaceIsItalic(FT_Face aFace)328 FTFaceIsItalic(FT_Face aFace)
329 {
330     return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
331 }
332 
333 static uint16_t
FTFaceGetWeight(FT_Face aFace)334 FTFaceGetWeight(FT_Face aFace)
335 {
336     TT_OS2 *os2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(aFace, ft_sfnt_os2));
337     uint16_t os2weight = 0;
338     if (os2 && os2->version != 0xffff) {
339         // Technically, only 100 to 900 are valid, but some fonts
340         // have this set wrong -- e.g. "Microsoft Logo Bold Italic" has
341         // it set to 6 instead of 600.  We try to be nice and handle that
342         // as well.
343         if (os2->usWeightClass >= 100 && os2->usWeightClass <= 900) {
344             os2weight = os2->usWeightClass;
345         } else if (os2->usWeightClass >= 1 && os2->usWeightClass <= 9) {
346             os2weight = os2->usWeightClass * 100;
347         }
348     }
349 
350     uint16_t result;
351     if (os2weight != 0) {
352         result = os2weight;
353     } else if (aFace->style_flags & FT_STYLE_FLAG_BOLD) {
354         result = 700;
355     } else {
356         result = 400;
357     }
358 
359     NS_ASSERTION(result >= 100 && result <= 900, "Invalid weight in font!");
360 
361     return result;
362 }
363 
364 // Used to create the font entry for installed faces on the device,
365 // when iterating over the fonts directories.
366 // We use the FT_Face to retrieve the details needed for the font entry,
367 // but unless we have been passed font data (i.e. for a user font),
368 // we do *not* save a reference to it, nor create a cairo face,
369 // as we don't want to keep a freetype face for every installed font
370 // permanently in memory.
371 /* static */
372 FT2FontEntry*
CreateFontEntry(FT_Face aFace,const char * aFilename,uint8_t aIndex,const nsAString & aName,const uint8_t * aFontData)373 FT2FontEntry::CreateFontEntry(FT_Face aFace,
374                               const char* aFilename, uint8_t aIndex,
375                               const nsAString& aName,
376                               const uint8_t* aFontData)
377 {
378     FT2FontEntry *fe = new FT2FontEntry(aName);
379     fe->mStyle = (FTFaceIsItalic(aFace) ?
380                   NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
381     fe->mWeight = FTFaceGetWeight(aFace);
382     fe->mFilename = aFilename;
383     fe->mFTFontIndex = aIndex;
384 
385     if (aFontData) {
386         fe->mFTFace = aFace;
387         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
388                     FT_LOAD_DEFAULT :
389                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
390         fe->mFontFace = cairo_ft_font_face_create_for_ft_face(aFace, flags);
391         FTUserFontData *userFontData = new FTUserFontData(aFace, aFontData);
392         cairo_font_face_set_user_data(fe->mFontFace, &sFTUserFontDataKey,
393                                       userFontData, FTFontDestroyFunc);
394     }
395 
396     return fe;
397 }
398 
399 // construct font entry name for an installed font from names in the FT_Face,
400 // and then create our FT2FontEntry
401 static FT2FontEntry*
CreateNamedFontEntry(FT_Face aFace,const char * aFilename,uint8_t aIndex)402 CreateNamedFontEntry(FT_Face aFace, const char* aFilename, uint8_t aIndex)
403 {
404     if (!aFace->family_name) {
405         return nullptr;
406     }
407     nsAutoString fontName;
408     AppendUTF8toUTF16(aFace->family_name, fontName);
409     if (aFace->style_name && strcmp("Regular", aFace->style_name)) {
410         fontName.Append(' ');
411         AppendUTF8toUTF16(aFace->style_name, fontName);
412     }
413     return FT2FontEntry::CreateFontEntry(aFace, aFilename, aIndex, fontName);
414 }
415 
416 FT2FontEntry*
GetFontEntry()417 gfxFT2Font::GetFontEntry()
418 {
419     return static_cast<FT2FontEntry*> (mFontEntry.get());
420 }
421 
422 cairo_font_face_t *
CairoFontFace()423 FT2FontEntry::CairoFontFace()
424 {
425     if (!mFontFace) {
426         AutoFTFace face(this);
427         if (!face) {
428             return nullptr;
429         }
430         mFTFace = face.forget();
431         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
432                     FT_LOAD_DEFAULT :
433                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
434         mFontFace = cairo_ft_font_face_create_for_ft_face(face, flags);
435         FTUserFontData *userFontData = new FTUserFontData(face, face.FontData());
436         cairo_font_face_set_user_data(mFontFace, &sFTUserFontDataKey,
437                                       userFontData, FTFontDestroyFunc);
438     }
439     return mFontFace;
440 }
441 
442 // Copied/modified from similar code in gfxMacPlatformFontList.mm:
443 // Complex scripts will not render correctly unless Graphite or OT
444 // layout tables are present.
445 // For OpenType, we also check that the GSUB table supports the relevant
446 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
447 // the characters, but lacks OpenType support).
448 
449 // TODO: consider whether we should move this to gfxFontEntry and do similar
450 // cmap-masking on all platforms to avoid using fonts that won't shape
451 // properly.
452 
453 nsresult
ReadCMAP(FontInfoData * aFontInfoData)454 FT2FontEntry::ReadCMAP(FontInfoData *aFontInfoData)
455 {
456     if (mCharacterMap) {
457         return NS_OK;
458     }
459 
460     RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
461 
462     AutoTArray<uint8_t, 16384> buffer;
463     nsresult rv = CopyFontTable(TTAG_cmap, buffer);
464 
465     if (NS_SUCCEEDED(rv)) {
466         bool unicodeFont;
467         bool symbolFont;
468         rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
469                                     *charmap, mUVSOffset,
470                                     unicodeFont, symbolFont);
471     }
472 
473     if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
474         // We assume a Graphite font knows what it's doing,
475         // and provides whatever shaping is needed for the
476         // characters it supports, so only check/clear the
477         // complex-script ranges for non-Graphite fonts
478 
479         // for layout support, check for the presence of opentype layout tables
480         bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
481 
482         for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
483              sr->rangeStart; sr++) {
484             // check to see if the cmap includes complex script codepoints
485             if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
486                 // We check for GSUB here, as GPOS alone would not be ok.
487                 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
488                     continue;
489                 }
490                 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
491             }
492         }
493     }
494 
495 #ifdef MOZ_WIDGET_ANDROID
496     // Hack for the SamsungDevanagari font, bug 1012365:
497     // pretend the font supports U+0972.
498     if (!charmap->test(0x0972) &&
499         charmap->test(0x0905) && charmap->test(0x0945)) {
500         charmap->set(0x0972);
501     }
502 #endif
503 
504     mHasCmapTable = NS_SUCCEEDED(rv);
505     if (mHasCmapTable) {
506         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
507         mCharacterMap = pfl->FindCharMap(charmap);
508     } else {
509         // if error occurred, initialize to null cmap
510         mCharacterMap = new gfxCharacterMap();
511     }
512     return rv;
513 }
514 
515 nsresult
CopyFontTable(uint32_t aTableTag,nsTArray<uint8_t> & aBuffer)516 FT2FontEntry::CopyFontTable(uint32_t aTableTag, nsTArray<uint8_t>& aBuffer)
517 {
518     AutoFTFace face(this);
519     if (!face) {
520         return NS_ERROR_FAILURE;
521     }
522 
523     FT_Error status;
524     FT_ULong len = 0;
525     status = FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &len);
526     if (status != FT_Err_Ok || len == 0) {
527         return NS_ERROR_FAILURE;
528     }
529 
530     if (!aBuffer.SetLength(len, fallible)) {
531         return NS_ERROR_OUT_OF_MEMORY;
532     }
533     uint8_t *buf = aBuffer.Elements();
534     status = FT_Load_Sfnt_Table(face, aTableTag, 0, buf, &len);
535     NS_ENSURE_TRUE(status == FT_Err_Ok, NS_ERROR_FAILURE);
536 
537     return NS_OK;
538 }
539 
540 hb_blob_t*
GetFontTable(uint32_t aTableTag)541 FT2FontEntry::GetFontTable(uint32_t aTableTag)
542 {
543     if (mFontFace) {
544         // if there's a cairo font face, we may be able to return a blob
545         // that just wraps a range of the attached user font data
546         FTUserFontData *userFontData = static_cast<FTUserFontData*>(
547             cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
548         if (userFontData && userFontData->FontData()) {
549             return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
550                                                       aTableTag);
551         }
552     }
553 
554     // otherwise, use the default method (which in turn will call our
555     // implementation of CopyFontTable)
556     return gfxFontEntry::GetFontTable(aTableTag);
557 }
558 
559 void
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const560 FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
561                                      FontListSizes* aSizes) const
562 {
563     gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
564     aSizes->mFontListSize +=
565         mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
566 }
567 
568 void
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const569 FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
570                                      FontListSizes* aSizes) const
571 {
572     aSizes->mFontListSize += aMallocSizeOf(this);
573     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
574 }
575 
576 /*
577  * FT2FontFamily
578  * A standard gfxFontFamily; just adds a method used to support sending
579  * the font list from chrome to content via IPC.
580  */
581 
582 void
AddFacesToFontList(InfallibleTArray<FontListEntry> * aFontList,Visibility aVisibility)583 FT2FontFamily::AddFacesToFontList(InfallibleTArray<FontListEntry>* aFontList,
584                                   Visibility aVisibility)
585 {
586     for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
587         const FT2FontEntry *fe =
588             static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
589         if (!fe) {
590             continue;
591         }
592 
593         aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
594                                                fe->mFilename,
595                                                fe->Weight(), fe->Stretch(),
596                                                fe->mStyle,
597                                                fe->mFTFontIndex,
598                                                aVisibility == kHidden));
599     }
600 }
601 
602 /*
603  * Startup cache support for the font list:
604  * We store the list of families and faces, with their style attributes and the
605  * corresponding font files, in the startup cache.
606  * This allows us to recreate the gfxFT2FontList collection of families and
607  * faces without instantiating Freetype faces for each font file (in order to
608  * find their attributes), leading to significantly quicker startup.
609  */
610 
611 #define CACHE_KEY "font.cached-list"
612 
613 class FontNameCache {
614 public:
615     // Creates the object but does NOT load the cached data from the startup
616     // cache; call Init() after creation to do that.
FontNameCache()617     FontNameCache()
618         : mMap(&mOps, sizeof(FNCMapEntry), 0)
619         , mWriteNeeded(false)
620     {
621         // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
622         // it to |mMap|'s constructor. A more normal approach here would be to
623         // have a static |sOps| member. Unfortunately, this mysteriously but
624         // consistently makes Fennec start-up slower, so we take this
625         // unorthodox approach instead. It's safe because PLDHashTable's
626         // constructor doesn't dereference the pointer; it just makes a copy of
627         // it.
628         mOps = (PLDHashTableOps) {
629             StringHash,
630             HashMatchEntry,
631             MoveEntry,
632             PLDHashTable::ClearEntryStub,
633             nullptr
634         };
635 
636         MOZ_ASSERT(XRE_IsParentProcess(),
637                    "FontNameCache should only be used in chrome process");
638         mCache = mozilla::scache::StartupCache::GetSingleton();
639     }
640 
~FontNameCache()641     ~FontNameCache()
642     {
643         if (!mWriteNeeded || !mCache) {
644             return;
645         }
646 
647         nsAutoCString buf;
648         for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
649             auto entry = static_cast<FNCMapEntry*>(iter.Get());
650             if (!entry->mFileExists) {
651                 // skip writing entries for files that are no longer present
652                 continue;
653             }
654             buf.Append(entry->mFilename);
655             buf.Append(';');
656             buf.Append(entry->mFaces);
657             buf.Append(';');
658             buf.AppendInt(entry->mTimestamp);
659             buf.Append(';');
660             buf.AppendInt(entry->mFilesize);
661             buf.Append(';');
662         }
663         mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
664     }
665 
666     // This may be called more than once (if we re-load the font list).
Init()667     void Init()
668     {
669         if (!mCache) {
670             return;
671         }
672 
673         uint32_t size;
674         UniquePtr<char[]> buf;
675         if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
676             return;
677         }
678 
679         LOG(("got: %s from the cache", nsDependentCString(buf.get(), size).get()));
680 
681         mMap.Clear();
682         mWriteNeeded = false;
683 
684         const char* beginning = buf.get();
685         const char* end = strchr(beginning, ';');
686         while (end) {
687             nsCString filename(beginning, end - beginning);
688             beginning = end + 1;
689             if (!(end = strchr(beginning, ';'))) {
690                 break;
691             }
692             nsCString faceList(beginning, end - beginning);
693             beginning = end + 1;
694             if (!(end = strchr(beginning, ';'))) {
695                 break;
696             }
697             uint32_t timestamp = strtoul(beginning, nullptr, 10);
698             beginning = end + 1;
699             if (!(end = strchr(beginning, ';'))) {
700                 break;
701             }
702             uint32_t filesize = strtoul(beginning, nullptr, 10);
703 
704             auto mapEntry =
705                 static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible));
706             if (mapEntry) {
707                 mapEntry->mFilename.Assign(filename);
708                 mapEntry->mTimestamp = timestamp;
709                 mapEntry->mFilesize = filesize;
710                 mapEntry->mFaces.Assign(faceList);
711                 // entries from the startupcache are marked "non-existing"
712                 // until we have confirmed that the file still exists
713                 mapEntry->mFileExists = false;
714             }
715 
716             beginning = end + 1;
717             end = strchr(beginning, ';');
718         }
719     }
720 
721     void
GetInfoForFile(const nsCString & aFileName,nsCString & aFaceList,uint32_t * aTimestamp,uint32_t * aFilesize)722     GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
723                    uint32_t *aTimestamp, uint32_t *aFilesize)
724     {
725         auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get()));
726         if (entry) {
727             *aTimestamp = entry->mTimestamp;
728             *aFilesize = entry->mFilesize;
729             aFaceList.Assign(entry->mFaces);
730             // this entry does correspond to an existing file
731             // (although it might not be up-to-date, in which case
732             // it will get overwritten via CacheFileInfo)
733             entry->mFileExists = true;
734         }
735     }
736 
737     void
CacheFileInfo(const nsCString & aFileName,const nsCString & aFaceList,uint32_t aTimestamp,uint32_t aFilesize)738     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
739                   uint32_t aTimestamp, uint32_t aFilesize)
740     {
741         auto entry =
742             static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible));
743         if (entry) {
744             entry->mFilename.Assign(aFileName);
745             entry->mTimestamp = aTimestamp;
746             entry->mFilesize = aFilesize;
747             entry->mFaces.Assign(aFaceList);
748             entry->mFileExists = true;
749         }
750         mWriteNeeded = true;
751     }
752 
753 private:
754     mozilla::scache::StartupCache* mCache;
755     PLDHashTable mMap;
756     bool mWriteNeeded;
757 
758     PLDHashTableOps mOps;
759 
760     typedef struct : public PLDHashEntryHdr {
761     public:
762         nsCString mFilename;
763         uint32_t  mTimestamp;
764         uint32_t  mFilesize;
765         nsCString mFaces;
766         bool      mFileExists;
767     } FNCMapEntry;
768 
StringHash(const void * key)769     static PLDHashNumber StringHash(const void *key)
770     {
771         return HashString(reinterpret_cast<const char*>(key));
772     }
773 
HashMatchEntry(const PLDHashEntryHdr * aHdr,const void * key)774     static bool HashMatchEntry(const PLDHashEntryHdr *aHdr, const void *key)
775     {
776         const FNCMapEntry* entry =
777             static_cast<const FNCMapEntry*>(aHdr);
778         return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
779     }
780 
MoveEntry(PLDHashTable * table,const PLDHashEntryHdr * aFrom,PLDHashEntryHdr * aTo)781     static void MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *aFrom,
782                           PLDHashEntryHdr *aTo)
783     {
784         FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
785         const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
786         to->mFilename.Assign(from->mFilename);
787         to->mTimestamp = from->mTimestamp;
788         to->mFilesize = from->mFilesize;
789         to->mFaces.Assign(from->mFaces);
790         to->mFileExists = from->mFileExists;
791     }
792 };
793 
794 /***************************************************************
795  *
796  * gfxFT2FontList
797  *
798  */
799 
800 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
801 // scanning the system's Fonts directory for OpenType and TrueType files.
802 
803 #define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
804 
805 class WillShutdownObserver : public nsIObserver
806 {
807 public:
808     NS_DECL_ISUPPORTS
809     NS_DECL_NSIOBSERVER
810 
WillShutdownObserver(gfxFT2FontList * aFontList)811     explicit WillShutdownObserver(gfxFT2FontList* aFontList)
812         : mFontList(aFontList)
813     { }
814 
815 protected:
~WillShutdownObserver()816     virtual ~WillShutdownObserver()
817     { }
818 
819     gfxFT2FontList *mFontList;
820 };
821 
NS_IMPL_ISUPPORTS(WillShutdownObserver,nsIObserver)822 NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver)
823 
824 NS_IMETHODIMP
825 WillShutdownObserver::Observe(nsISupports *aSubject,
826                               const char *aTopic,
827                               const char16_t *aData)
828 {
829     if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
830         mFontList->WillShutdown();
831     } else {
832         NS_NOTREACHED("unexpected notification topic");
833     }
834     return NS_OK;
835 }
836 
gfxFT2FontList()837 gfxFT2FontList::gfxFT2FontList()
838     : mJarModifiedTime(0)
839 {
840     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
841     if (obs) {
842         mObserver = new WillShutdownObserver(this);
843         obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
844     }
845 }
846 
~gfxFT2FontList()847 gfxFT2FontList::~gfxFT2FontList()
848 {
849     if (mObserver) {
850         nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
851         if (obs) {
852             obs->RemoveObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
853         }
854         mObserver = nullptr;
855     }
856 }
857 
858 void
AppendFacesFromCachedFaceList(const nsCString & aFileName,const nsCString & aFaceList,StandardFile aStdFile,FT2FontFamily::Visibility aVisibility)859 gfxFT2FontList::AppendFacesFromCachedFaceList(
860     const nsCString& aFileName,
861     const nsCString& aFaceList,
862     StandardFile aStdFile,
863     FT2FontFamily::Visibility aVisibility)
864 {
865     const char *beginning = aFaceList.get();
866     const char *end = strchr(beginning, ',');
867     while (end) {
868         NS_ConvertUTF8toUTF16 familyName(beginning, end - beginning);
869         ToLowerCase(familyName);
870         beginning = end + 1;
871         if (!(end = strchr(beginning, ','))) {
872             break;
873         }
874         NS_ConvertUTF8toUTF16 faceName(beginning, end - beginning);
875         beginning = end + 1;
876         if (!(end = strchr(beginning, ','))) {
877             break;
878         }
879         uint32_t index = strtoul(beginning, nullptr, 10);
880         beginning = end + 1;
881         if (!(end = strchr(beginning, ','))) {
882             break;
883         }
884         bool italic = (*beginning != '0');
885         beginning = end + 1;
886         if (!(end = strchr(beginning, ','))) {
887             break;
888         }
889         uint32_t weight = strtoul(beginning, nullptr, 10);
890         beginning = end + 1;
891         if (!(end = strchr(beginning, ','))) {
892             break;
893         }
894         int32_t stretch = strtol(beginning, nullptr, 10);
895 
896         FontListEntry fle(familyName, faceName, aFileName,
897                           weight, stretch, italic, index,
898                           aVisibility == FT2FontFamily::kHidden);
899         AppendFaceFromFontListEntry(fle, aStdFile);
900 
901         beginning = end + 1;
902         end = strchr(beginning, ',');
903     }
904 }
905 
906 static void
AppendToFaceList(nsCString & aFaceList,nsAString & aFamilyName,FT2FontEntry * aFontEntry)907 AppendToFaceList(nsCString& aFaceList,
908                  nsAString& aFamilyName, FT2FontEntry* aFontEntry)
909 {
910     aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
911     aFaceList.Append(',');
912     aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
913     aFaceList.Append(',');
914     aFaceList.AppendInt(aFontEntry->mFTFontIndex);
915     aFaceList.Append(',');
916     aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
917     aFaceList.Append(',');
918     aFaceList.AppendInt(aFontEntry->Weight());
919     aFaceList.Append(',');
920     aFaceList.AppendInt(aFontEntry->Stretch());
921     aFaceList.Append(',');
922 }
923 
924 void
CheckForBrokenFont(gfxFontFamily * aFamily)925 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
926 {
927     // note if the family is in the "bad underline" blacklist
928     if (aFamily->IsBadUnderlineFamily()) {
929         mIsBadUnderlineFont = true;
930     }
931 
932     // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
933     // because of unwanted on-by-default "ae" ligature.
934     // (See also AppendFaceFromFontListEntry.)
935     if (aFamily->Name().EqualsLiteral("roboto")) {
936         mIgnoreGSUB = true;
937     }
938 
939     // bug 706888 - set the IgnoreGSUB flag on the broken version of
940     // Droid Sans Arabic from certain phones, as identified by the
941     // font checksum in the 'head' table
942     else if (aFamily->Name().EqualsLiteral("droid sans arabic")) {
943         AutoFTFace face(this);
944         if (face) {
945             const TT_Header *head = static_cast<const TT_Header*>
946                 (FT_Get_Sfnt_Table(face, ft_sfnt_head));
947             if (head && head->CheckSum_Adjust == 0xe445242) {
948                 mIgnoreGSUB = true;
949             }
950         }
951     }
952 }
953 
954 void
AppendFacesFromFontFile(const nsCString & aFileName,FontNameCache * aCache,StandardFile aStdFile,FT2FontFamily::Visibility aVisibility)955 gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
956                                         FontNameCache *aCache,
957                                         StandardFile aStdFile,
958                                         FT2FontFamily::Visibility aVisibility)
959 {
960     nsCString cachedFaceList;
961     uint32_t filesize = 0, timestamp = 0;
962     if (aCache) {
963         aCache->GetInfoForFile(aFileName, cachedFaceList, &timestamp, &filesize);
964     }
965 
966     struct stat s;
967     int statRetval = stat(aFileName.get(), &s);
968     if (!cachedFaceList.IsEmpty() && 0 == statRetval &&
969         s.st_mtime == timestamp && s.st_size == filesize)
970     {
971         LOG(("using cached font info for %s", aFileName.get()));
972         AppendFacesFromCachedFaceList(aFileName, cachedFaceList, aStdFile,
973                                       aVisibility);
974         return;
975     }
976 
977     FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
978     FT_Face dummy;
979     if (FT_Err_Ok == FT_New_Face(ftLibrary, aFileName.get(), -1, &dummy)) {
980         LOG(("reading font info via FreeType for %s", aFileName.get()));
981         nsCString newFaceList;
982         timestamp = s.st_mtime;
983         filesize = s.st_size;
984         for (FT_Long i = 0; i < dummy->num_faces; i++) {
985             FT_Face face;
986             if (FT_Err_Ok != FT_New_Face(ftLibrary, aFileName.get(), i, &face)) {
987                 continue;
988             }
989             AddFaceToList(aFileName, i, aStdFile, aVisibility, face, newFaceList);
990             FT_Done_Face(face);
991         }
992         FT_Done_Face(dummy);
993         if (aCache && 0 == statRetval && !newFaceList.IsEmpty()) {
994             aCache->CacheFileInfo(aFileName, newFaceList, timestamp, filesize);
995         }
996     }
997 }
998 
999 void
FindFontsInOmnijar(FontNameCache * aCache)1000 gfxFT2FontList::FindFontsInOmnijar(FontNameCache *aCache)
1001 {
1002     bool jarChanged = false;
1003 
1004     mozilla::scache::StartupCache* cache =
1005         mozilla::scache::StartupCache::GetSingleton();
1006     UniquePtr<char[]> cachedModifiedTimeBuf;
1007     uint32_t longSize;
1008     if (cache &&
1009         NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
1010                                       &cachedModifiedTimeBuf,
1011                                       &longSize)) &&
1012         longSize == sizeof(int64_t))
1013     {
1014         nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
1015         jarFile->GetLastModifiedTime(&mJarModifiedTime);
1016         if (mJarModifiedTime > *(int64_t*)cachedModifiedTimeBuf.get()) {
1017             jarChanged = true;
1018         }
1019     }
1020 
1021     static const char* sJarSearchPaths[] = {
1022         "res/fonts/*.ttf$",
1023     };
1024     RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
1025     for (unsigned i = 0; i < ArrayLength(sJarSearchPaths); i++) {
1026         nsZipFind* find;
1027         if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
1028             const char* path;
1029             uint16_t len;
1030             while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
1031                 nsCString entryName(path, len);
1032                 AppendFacesFromOmnijarEntry(reader, entryName, aCache,
1033                                             jarChanged);
1034             }
1035             delete find;
1036         }
1037     }
1038 }
1039 
1040 // Given the freetype face corresponding to an entryName and face index,
1041 // add the face to the available font list and to the faceList string
1042 void
AddFaceToList(const nsCString & aEntryName,uint32_t aIndex,StandardFile aStdFile,FT2FontFamily::Visibility aVisibility,FT_Face aFace,nsCString & aFaceList)1043 gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
1044                               StandardFile aStdFile,
1045                               FT2FontFamily::Visibility aVisibility,
1046                               FT_Face aFace,
1047                               nsCString& aFaceList)
1048 {
1049     if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
1050         // ignore faces that don't support a Unicode charmap
1051         return;
1052     }
1053 
1054     // build the font entry name and create an FT2FontEntry,
1055     // but do -not- keep a reference to the FT_Face
1056     RefPtr<FT2FontEntry> fe =
1057         CreateNamedFontEntry(aFace, aEntryName.get(), aIndex);
1058 
1059     auto& fontFamilies =
1060         (aVisibility == FT2FontFamily::kHidden) ? mHiddenFontFamilies :
1061                                                   mFontFamilies;
1062 
1063     if (fe) {
1064         NS_ConvertUTF8toUTF16 name(aFace->family_name);
1065         BuildKeyNameFromFontName(name);
1066         RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
1067         if (!family) {
1068             family = new FT2FontFamily(name);
1069             fontFamilies.Put(name, family);
1070             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
1071                 family->SetSkipSpaceFeatureCheck(true);
1072             }
1073             if (mBadUnderlineFamilyNames.Contains(name)) {
1074                 family->SetBadUnderlineFamily();
1075             }
1076         }
1077         fe->mStandardFace = (aStdFile == kStandard);
1078         family->AddFontEntry(fe);
1079 
1080         fe->CheckForBrokenFont(family);
1081 
1082         AppendToFaceList(aFaceList, name, fe);
1083         if (LOG_ENABLED()) {
1084             LOG(("(fontinit) added (%s) to family (%s)"
1085                  " with style: %s weight: %d stretch: %d",
1086                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
1087                  NS_ConvertUTF16toUTF8(family->Name()).get(),
1088                  fe->IsItalic() ? "italic" : "normal",
1089                  fe->Weight(), fe->Stretch()));
1090         }
1091     }
1092 }
1093 
1094 void
AppendFacesFromOmnijarEntry(nsZipArchive * aArchive,const nsCString & aEntryName,FontNameCache * aCache,bool aJarChanged)1095 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
1096                                             const nsCString& aEntryName,
1097                                             FontNameCache *aCache,
1098                                             bool aJarChanged)
1099 {
1100     nsCString faceList;
1101     if (aCache && !aJarChanged) {
1102         uint32_t filesize, timestamp;
1103         aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
1104         if (faceList.Length() > 0) {
1105             AppendFacesFromCachedFaceList(aEntryName, faceList);
1106             return;
1107         }
1108     }
1109 
1110     nsZipItem *item = aArchive->GetItem(aEntryName.get());
1111     NS_ASSERTION(item, "failed to find zip entry");
1112 
1113     uint32_t bufSize = item->RealSize();
1114     // We use fallible allocation here; if there's not enough RAM, we'll simply
1115     // ignore the bundled fonts and fall back to the device's installed fonts.
1116     auto buf = MakeUniqueFallible<uint8_t[]>(bufSize);
1117     if (!buf) {
1118         return;
1119     }
1120 
1121     nsZipCursor cursor(item, aArchive, buf.get(), bufSize);
1122     uint8_t* data = cursor.Copy(&bufSize);
1123     NS_ASSERTION(data && bufSize == item->RealSize(),
1124                  "error reading bundled font");
1125     if (!data) {
1126         return;
1127     }
1128 
1129     FT_Library ftLibrary = gfxAndroidPlatform::GetPlatform()->GetFTLibrary();
1130 
1131     FT_Face dummy;
1132     if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, 0, &dummy)) {
1133         return;
1134     }
1135 
1136     for (FT_Long i = 0; i < dummy->num_faces; i++) {
1137         FT_Face face;
1138         if (FT_Err_Ok != FT_New_Memory_Face(ftLibrary, buf.get(), bufSize, i, &face)) {
1139             continue;
1140         }
1141         AddFaceToList(aEntryName, i, kStandard, FT2FontFamily::kVisible,
1142                       face, faceList);
1143         FT_Done_Face(face);
1144     }
1145 
1146     FT_Done_Face(dummy);
1147 
1148     if (aCache && !faceList.IsEmpty()) {
1149         aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
1150     }
1151 }
1152 
1153 // Called on each family after all fonts are added to the list;
1154 // this will sort faces to give priority to "standard" font files
1155 // if aUserArg is non-null (i.e. we're using it as a boolean flag)
1156 static void
FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,RefPtr<gfxFontFamily> & aFamily,bool aSortFaces)1157 FinalizeFamilyMemberList(nsStringHashKey::KeyType aKey,
1158                          RefPtr<gfxFontFamily>& aFamily,
1159                          bool aSortFaces)
1160 {
1161     gfxFontFamily *family = aFamily.get();
1162 
1163     family->SetHasStyles(true);
1164 
1165     if (aSortFaces) {
1166         family->SortAvailableFonts();
1167     }
1168     family->CheckForSimpleFamily();
1169 }
1170 
1171 void
FindFonts()1172 gfxFT2FontList::FindFonts()
1173 {
1174     gfxFontCache *fc = gfxFontCache::GetCache();
1175     if (fc)
1176         fc->AgeAllGenerations();
1177     ClearLangGroupPrefFonts();
1178     mCodepointsWithNoFonts.reset();
1179 
1180     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
1181     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
1182 
1183     if (!XRE_IsParentProcess()) {
1184         // Content process: ask the Chrome process to give us the list
1185         InfallibleTArray<FontListEntry> fonts;
1186         mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts);
1187         for (uint32_t i = 0, n = fonts.Length(); i < n; ++i) {
1188             // We don't need to identify "standard" font files here,
1189             // as the faces are already sorted.
1190             AppendFaceFromFontListEntry(fonts[i], kUnknown);
1191         }
1192         // Passing null for userdata tells Finalize that it does not need
1193         // to sort faces (because they were already sorted by chrome,
1194         // so we just maintain the existing order)
1195         for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1196             nsStringHashKey::KeyType key = iter.Key();
1197             RefPtr<gfxFontFamily>& family = iter.Data();
1198             FinalizeFamilyMemberList(key, family, /* aSortFaces */ false);
1199         }
1200         for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1201             nsStringHashKey::KeyType key = iter.Key();
1202             RefPtr<gfxFontFamily>& family = iter.Data();
1203             FinalizeFamilyMemberList(key, family, /* aSortFaces */ false );
1204         }
1205 
1206         LOG(("got font list from chrome process: %d faces in %d families "
1207              "and %d in hidden families",
1208             fonts.Length(), mFontFamilies.Count(),
1209             mHiddenFontFamilies.Count()));
1210         return;
1211     }
1212 
1213     // Chrome process: get the cached list (if any)
1214     if (!mFontNameCache) {
1215         mFontNameCache = MakeUnique<FontNameCache>();
1216     }
1217     mFontNameCache->Init();
1218 
1219     // ANDROID_ROOT is the root of the android system, typically /system;
1220     // font files are in /$ANDROID_ROOT/fonts/
1221     nsCString root;
1222     char *androidRoot = PR_GetEnv("ANDROID_ROOT");
1223     if (androidRoot) {
1224         root = androidRoot;
1225     } else {
1226         root = NS_LITERAL_CSTRING("/system");
1227     }
1228     root.AppendLiteral("/fonts");
1229 
1230     FindFontsInDir(root, mFontNameCache.get(), FT2FontFamily::kVisible);
1231 
1232     if (mFontFamilies.Count() == 0) {
1233         // if we can't find/read the font directory, we are doomed!
1234         NS_RUNTIMEABORT("Could not read the system fonts directory");
1235     }
1236 
1237     // Look for fonts stored in omnijar, unless we're on a low-memory
1238     // device where we don't want to spend the RAM to decompress them.
1239     // (Prefs may disable this, or force-enable it even with low memory.)
1240     bool lowmem;
1241     nsCOMPtr<nsIMemory> mem = nsMemory::GetGlobalMemoryService();
1242     if ((NS_SUCCEEDED(mem->IsLowMemoryPlatform(&lowmem)) && !lowmem &&
1243          Preferences::GetBool("gfx.bundled_fonts.enabled")) ||
1244         Preferences::GetBool("gfx.bundled_fonts.force-enabled")) {
1245         FindFontsInOmnijar(mFontNameCache.get());
1246     }
1247 
1248     // Look for downloaded fonts in a profile-agnostic "fonts" directory.
1249     nsCOMPtr<nsIProperties> dirSvc =
1250       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1251     if (dirSvc) {
1252         nsCOMPtr<nsIFile> appDir;
1253         nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
1254                          NS_GET_IID(nsIFile), getter_AddRefs(appDir));
1255         if (NS_SUCCEEDED(rv)) {
1256             appDir->AppendNative(NS_LITERAL_CSTRING("fonts"));
1257             nsCString localPath;
1258             if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
1259                 FindFontsInDir(localPath, mFontNameCache.get(),
1260                                FT2FontFamily::kVisible);
1261             }
1262         }
1263     }
1264 
1265     // look for locally-added fonts in a "fonts" subdir of the profile
1266     nsCOMPtr<nsIFile> localDir;
1267     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
1268                                          getter_AddRefs(localDir));
1269     if (NS_SUCCEEDED(rv) &&
1270         NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1271         nsCString localPath;
1272         rv = localDir->GetNativePath(localPath);
1273         if (NS_SUCCEEDED(rv)) {
1274             FindFontsInDir(localPath, mFontNameCache.get(),
1275                            FT2FontFamily::kVisible);
1276         }
1277     }
1278 
1279     // Finalize the families by sorting faces into standard order
1280     // and marking "simple" families.
1281     // Passing non-null userData here says that we want faces to be sorted.
1282     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1283         nsStringHashKey::KeyType key = iter.Key();
1284         RefPtr<gfxFontFamily>& family = iter.Data();
1285         FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
1286     }
1287     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1288         nsStringHashKey::KeyType key = iter.Key();
1289         RefPtr<gfxFontFamily>& family = iter.Data();
1290         FinalizeFamilyMemberList(key, family, /* aSortFaces */ true);
1291     }
1292 }
1293 
1294 void
FindFontsInDir(const nsCString & aDir,FontNameCache * aFNC,FT2FontFamily::Visibility aVisibility)1295 gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
1296                                FontNameCache *aFNC,
1297                                FT2FontFamily::Visibility aVisibility)
1298 {
1299     static const char* sStandardFonts[] = {
1300         "DroidSans.ttf",
1301         "DroidSans-Bold.ttf",
1302         "DroidSerif-Regular.ttf",
1303         "DroidSerif-Bold.ttf",
1304         "DroidSerif-Italic.ttf",
1305         "DroidSerif-BoldItalic.ttf",
1306         "DroidSansMono.ttf",
1307         "DroidSansArabic.ttf",
1308         "DroidSansHebrew.ttf",
1309         "DroidSansThai.ttf",
1310         "MTLmr3m.ttf",
1311         "MTLc3m.ttf",
1312         "NanumGothic.ttf",
1313         "DroidSansJapanese.ttf",
1314         "DroidSansFallback.ttf"
1315     };
1316 
1317     DIR *d = opendir(aDir.get());
1318     if (!d) {
1319         return;
1320     }
1321 
1322     struct dirent *ent = nullptr;
1323     while ((ent = readdir(d)) != nullptr) {
1324         const char *ext = strrchr(ent->d_name, '.');
1325         if (!ext) {
1326             continue;
1327         }
1328         if (strcasecmp(ext, ".ttf") == 0 ||
1329             strcasecmp(ext, ".otf") == 0 ||
1330             strcasecmp(ext, ".woff") == 0 ||
1331             strcasecmp(ext, ".ttc") == 0) {
1332             bool isStdFont = false;
1333             for (unsigned int i = 0;
1334                  i < ArrayLength(sStandardFonts) && !isStdFont; i++) {
1335                 isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
1336             }
1337 
1338             nsCString s(aDir);
1339             s.Append('/');
1340             s.Append(ent->d_name);
1341 
1342             // Add the face(s) from this file to our font list;
1343             // note that if we have cached info for this file in fnc,
1344             // and the file is unchanged, we won't actually need to read it.
1345             // If the file is new/changed, this will update the FontNameCache.
1346             AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown,
1347                                     aVisibility);
1348         }
1349     }
1350 
1351     closedir(d);
1352 }
1353 
1354 void
AppendFaceFromFontListEntry(const FontListEntry & aFLE,StandardFile aStdFile)1355 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
1356                                             StandardFile aStdFile)
1357 {
1358     FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
1359     if (fe) {
1360         auto& fontFamilies =
1361             aFLE.isHidden() ? mHiddenFontFamilies : mFontFamilies;
1362         fe->mStandardFace = (aStdFile == kStandard);
1363         nsAutoString name(aFLE.familyName());
1364         RefPtr<gfxFontFamily> family = fontFamilies.GetWeak(name);
1365         if (!family) {
1366             family = new FT2FontFamily(name);
1367             fontFamilies.Put(name, family);
1368             if (mSkipSpaceLookupCheckFamilies.Contains(name)) {
1369                 family->SetSkipSpaceFeatureCheck(true);
1370             }
1371             if (mBadUnderlineFamilyNames.Contains(name)) {
1372                 family->SetBadUnderlineFamily();
1373             }
1374         }
1375         family->AddFontEntry(fe);
1376 
1377         fe->CheckForBrokenFont(family);
1378     }
1379 }
1380 
1381 void
GetSystemFontList(InfallibleTArray<FontListEntry> * retValue)1382 gfxFT2FontList::GetSystemFontList(InfallibleTArray<FontListEntry>* retValue)
1383 {
1384     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1385         auto family = static_cast<FT2FontFamily*>(iter.Data().get());
1386         family->AddFacesToFontList(retValue, FT2FontFamily::kVisible);
1387     }
1388     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1389         auto family = static_cast<FT2FontFamily*>(iter.Data().get());
1390         family->AddFacesToFontList(retValue, FT2FontFamily::kHidden);
1391     }
1392 }
1393 
1394 static void
LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey> & aSkipSpaceLookupCheck)1395 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
1396 {
1397     AutoTArray<nsString, 5> skiplist;
1398     gfxFontUtils::GetPrefsFontList(
1399         "font.whitelist.skip_default_features_space_check",
1400         skiplist);
1401     uint32_t numFonts = skiplist.Length();
1402     for (uint32_t i = 0; i < numFonts; i++) {
1403         ToLowerCase(skiplist[i]);
1404         aSkipSpaceLookupCheck.PutEntry(skiplist[i]);
1405     }
1406 }
1407 
1408 void
PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,RefPtr<gfxFontFamily> & aFamily)1409 PreloadAsUserFontFaces(nsStringHashKey::KeyType aKey,
1410                        RefPtr<gfxFontFamily>& aFamily)
1411 {
1412     gfxFontFamily *family = aFamily.get();
1413 
1414     auto& faces = family->GetFontList();
1415     size_t count = faces.Length();
1416     for (size_t i = 0; i < count; ++i) {
1417         FT2FontEntry* fe = static_cast<FT2FontEntry*>(faces[i].get());
1418         if (fe->mFTFontIndex != 0) {
1419             NS_NOTREACHED("don't try to preload a multi-face font");
1420             continue;
1421         }
1422 
1423         // XXX Should we move the i/o here off the main thread?
1424 
1425         // Map the font data in fe->mFilename, so we can calculate its CRC32.
1426         int fd = open(fe->mFilename.get(), O_RDONLY);
1427         if (fd < 0) {
1428             continue;
1429         }
1430         struct stat buf;
1431         if (fstat(fd, &buf) != 0 || buf.st_size < 12) {
1432             close(fd);
1433             continue;
1434         }
1435         char* data = static_cast<char*>(
1436             mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
1437         close(fd);
1438         if (data == MAP_FAILED) {
1439             continue;
1440         }
1441 
1442         // Calculate CRC32
1443         uint32_t crc = crc32(0, nullptr, 0);
1444         crc = crc32(crc, (Bytef*)data, buf.st_size);
1445         munmap(data, buf.st_size);
1446 
1447 #if 0
1448         ALOG("\n**** Preloading family [%s] face [%s] CRC32 [0x%08x]",
1449              NS_ConvertUTF16toUTF8(family->Name()).get(),
1450              fe->mFilename.get(),
1451              crc);
1452 #endif
1453 
1454         fe->mUserFontData = MakeUnique<gfxUserFontData>();
1455         fe->mUserFontData->mRealName = fe->Name();
1456         fe->mUserFontData->mCRC32 = crc;
1457         fe->mUserFontData->mLength = buf.st_size;
1458 
1459         // Stash it persistently in the user-font cache.
1460         gfxUserFontSet::UserFontCache::CacheFont(
1461             fe, gfxUserFontSet::UserFontCache::kPersistent);
1462     }
1463 }
1464 
1465 nsresult
InitFontListForPlatform()1466 gfxFT2FontList::InitFontListForPlatform()
1467 {
1468     // reset hidden font list
1469     mHiddenFontFamilies.Clear();
1470 
1471     LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
1472 
1473     FindFonts();
1474 
1475     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1476         nsStringHashKey::KeyType key = iter.Key();
1477         RefPtr<gfxFontFamily>& family = iter.Data();
1478         PreloadAsUserFontFaces(key, family);
1479     }
1480     return NS_OK;
1481 }
1482 
1483 // called for each family name, based on the assumption that the
1484 // first part of the full name is the family name
1485 
1486 gfxFontEntry*
LookupLocalFont(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle)1487 gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
1488                                 uint16_t aWeight,
1489                                 int16_t aStretch,
1490                                 uint8_t aStyle)
1491 {
1492     // walk over list of names
1493     FT2FontEntry* fontEntry = nullptr;
1494     nsString fullName(aFontName);
1495 
1496     // Note that we only check mFontFamilies here, not mHiddenFontFamilies;
1497     // hence @font-face { src:local(...) } will not find hidden fonts.
1498     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1499         // Check family name, based on the assumption that the
1500         // first part of the full name is the family name
1501         RefPtr<gfxFontFamily>& fontFamily = iter.Data();
1502 
1503         // does the family name match up to the length of the family name?
1504         const nsString& family = fontFamily->Name();
1505         nsString fullNameFamily;
1506 
1507         fullName.Left(fullNameFamily, family.Length());
1508 
1509         // if so, iterate over faces in this family to see if there is a match
1510         if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
1511             nsTArray<RefPtr<gfxFontEntry> >& fontList = fontFamily->GetFontList();
1512             int index, len = fontList.Length();
1513             for (index = 0; index < len; index++) {
1514                 gfxFontEntry* fe = fontList[index];
1515                 if (!fe) {
1516                     continue;
1517                 }
1518                 if (fe->Name().Equals(fullName,
1519                                       nsCaseInsensitiveStringComparator())) {
1520                     fontEntry = static_cast<FT2FontEntry*>(fe);
1521                     goto searchDone;
1522                 }
1523             }
1524         }
1525     }
1526 
1527 searchDone:
1528     if (!fontEntry) {
1529         return nullptr;
1530     }
1531 
1532     // Clone the font entry so that we can then set its style descriptors
1533     // from the userfont entry rather than the actual font.
1534 
1535     // Ensure existence of mFTFace in the original entry
1536     fontEntry->CairoFontFace();
1537     if (!fontEntry->mFTFace) {
1538         return nullptr;
1539     }
1540 
1541     FT2FontEntry* fe =
1542         FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
1543                                       fontEntry->mFilename.get(),
1544                                       fontEntry->mFTFontIndex,
1545                                       fontEntry->Name(), nullptr);
1546     if (fe) {
1547         fe->mStyle = aStyle;
1548         fe->mWeight = aWeight;
1549         fe->mStretch = aStretch;
1550         fe->mIsLocalUserFont = true;
1551     }
1552 
1553     return fe;
1554 }
1555 
1556 gfxFontFamily*
GetDefaultFontForPlatform(const gfxFontStyle * aStyle)1557 gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
1558 {
1559     gfxFontFamily *ff = nullptr;
1560 #if defined(MOZ_WIDGET_ANDROID)
1561     ff = FindFamily(NS_LITERAL_STRING("Roboto"));
1562     if (!ff) {
1563         ff = FindFamily(NS_LITERAL_STRING("Droid Sans"));
1564     }
1565 #endif
1566     /* TODO: what about Qt or other platforms that may use this? */
1567     return ff;
1568 }
1569 
1570 gfxFontEntry*
MakePlatformFont(const nsAString & aFontName,uint16_t aWeight,int16_t aStretch,uint8_t aStyle,const uint8_t * aFontData,uint32_t aLength)1571 gfxFT2FontList::MakePlatformFont(const nsAString& aFontName,
1572                                  uint16_t aWeight,
1573                                  int16_t aStretch,
1574                                  uint8_t aStyle,
1575                                  const uint8_t* aFontData,
1576                                  uint32_t aLength)
1577 {
1578     // The FT2 font needs the font data to persist, so we do NOT free it here
1579     // but instead pass ownership to the font entry.
1580     // Deallocation will happen later, when the font face is destroyed.
1581     return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
1582                                          aStyle, aFontData, aLength);
1583 }
1584 
1585 void
GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily>> & aFamilyArray)1586 gfxFT2FontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
1587 {
1588     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1589         RefPtr<gfxFontFamily>& family = iter.Data();
1590         aFamilyArray.AppendElement(family);
1591     }
1592     for (auto iter = mHiddenFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1593         RefPtr<gfxFontFamily>& family = iter.Data();
1594         aFamilyArray.AppendElement(family);
1595     }
1596 }
1597 
1598 void
WillShutdown()1599 gfxFT2FontList::WillShutdown()
1600 {
1601     mozilla::scache::StartupCache* cache =
1602         mozilla::scache::StartupCache::GetSingleton();
1603     if (cache && mJarModifiedTime > 0) {
1604         cache->PutBuffer(JAR_LAST_MODIFED_TIME,
1605                          (char*)&mJarModifiedTime, sizeof(mJarModifiedTime));
1606     }
1607     mFontNameCache = nullptr;
1608 }
1609