Tue, 06 Jan 2015 21:39:09 +0100
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, ×tamp, &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))
1009 {
1010 nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
1011 jarFile->GetLastModifiedTime(&jarModifiedTime);
1012 if (jarModifiedTime > *(int64_t*)cachedModifiedTimeBuf) {
1013 jarChanged = true;
1014 }
1015 }
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);
1030 }
1031 delete find;
1032 }
1033 }
1035 if (cache) {
1036 cache->PutBuffer(JAR_LAST_MODIFED_TIME, (char*)&jarModifiedTime,
1037 sizeof(jarModifiedTime));
1038 }
1039 }
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)
1047 {
1048 if (FT_Err_Ok != FT_Select_Charmap(aFace, FT_ENCODING_UNICODE)) {
1049 // ignore faces that don't support a Unicode charmap
1050 return;
1051 }
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);
1067 }
1068 if (mBadUnderlineFamilyNames.Contains(name)) {
1069 family->SetBadUnderlineFamily();
1070 }
1071 }
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()));
1086 }
1087 #endif
1088 }
1089 }
1091 void
1092 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
1093 const nsCString& aEntryName,
1094 FontNameCache *aCache,
1095 bool aJarChanged)
1096 {
1097 nsCString faceList;
1098 if (aCache && !aJarChanged) {
1099 uint32_t filesize, timestamp;
1100 aCache->GetInfoForFile(aEntryName, faceList, ×tamp, &filesize);
1101 if (faceList.Length() > 0) {
1102 AppendFacesFromCachedFaceList(aEntryName, true, faceList);
1103 return;
1104 }
1105 }
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;
1116 }
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;
1124 }
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;
1131 }
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;
1137 }
1138 AddFaceToList(aEntryName, i, true, face, faceList);
1139 FT_Done_Face(face);
1140 }
1142 FT_Done_Face(dummy);
1144 if (aCache && !faceList.IsEmpty()) {
1145 aCache->CacheFileInfo(aEntryName, faceList, 0, bufSize);
1146 }
1147 }
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)
1156 {
1157 gfxFontFamily *family = aFamily.get();
1158 bool sortFaces = (aUserArg != nullptr);
1160 family->SetHasStyles(true);
1162 if (sortFaces) {
1163 family->SortAvailableFonts();
1164 }
1165 family->CheckForSimpleFamily();
1167 return PL_DHASH_NEXT;
1168 }
1170 void
1171 gfxFT2FontList::FindFonts()
1172 {
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);
1191 }
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);
1211 }
1212 if (handle != INVALID_HANDLE_VALUE)
1213 FindClose(handle);
1214 }
1215 }
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);
1232 }
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;
1240 }
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");
1253 }
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");
1261 }
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);
1273 }
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);
1285 }
1286 }
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);
1292 }
1294 #ifdef ANDROID
1295 void
1296 gfxFT2FontList::FindFontsInDir(const nsCString& aDir, FontNameCache *aFNC)
1297 {
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;
1319 }
1321 struct dirent *ent = nullptr;
1322 while ((ent = readdir(d)) != nullptr) {
1323 const char *ext = strrchr(ent->d_name, '.');
1324 if (!ext) {
1325 continue;
1326 }
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;
1335 }
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);
1346 }
1347 }
1349 closedir(d);
1350 }
1351 #endif
1353 void
1354 gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
1355 bool aStdFile)
1356 {
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);
1367 }
1368 if (mBadUnderlineFamilyNames.Contains(name)) {
1369 family->SetBadUnderlineFamily();
1370 }
1371 }
1372 family->AddFontEntry(fe);
1374 fe->CheckForBrokenFont(family);
1375 }
1376 }
1378 static PLDHashOperator
1379 AddFamilyToFontList(nsStringHashKey::KeyType aKey,
1380 nsRefPtr<gfxFontFamily>& aFamily,
1381 void* aUserArg)
1382 {
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;
1390 }
1392 void
1393 gfxFT2FontList::GetFontList(InfallibleTArray<FontListEntry>* retValue)
1394 {
1395 mFontFamilies.Enumerate(AddFamilyToFontList, retValue);
1396 }
1398 static void
1399 LoadSkipSpaceLookupCheck(nsTHashtable<nsStringHashKey>& aSkipSpaceLookupCheck)
1400 {
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]);
1409 }
1410 }
1412 nsresult
1413 gfxFT2FontList::InitFontList()
1414 {
1415 // reset font lists
1416 gfxPlatformFontList::InitFontList();
1418 LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
1420 FindFonts();
1422 return NS_OK;
1423 }
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)
1440 {
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;
1457 }
1458 if (fe->Name().Equals(data->mFullName,
1459 nsCaseInsensitiveStringComparator())) {
1460 data->mFontEntry = static_cast<FT2FontEntry*>(fe);
1461 return PL_DHASH_STOP;
1462 }
1463 }
1464 }
1466 return PL_DHASH_NEXT;
1467 }
1469 gfxFontEntry*
1470 gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
1471 const nsAString& aFontName)
1472 {
1473 // walk over list of names
1474 FullFontNameSearch data(aFontName);
1476 mFontFamilies.Enumerate(FindFullName, &data);
1478 if (!data.mFontEntry) {
1479 return nullptr;
1480 }
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;
1489 }
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;
1501 }
1503 return fe;
1504 }
1506 gfxFontFamily*
1507 gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle)
1508 {
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);
1516 }
1517 }
1518 #elif defined(MOZ_WIDGET_GONK)
1519 nsAutoString resolvedName;
1520 if (ResolveFontName(NS_LITERAL_STRING("Fira Sans OT"), resolvedName)) {
1521 return FindFamily(resolvedName);
1522 }
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);
1528 }
1529 #endif
1530 /* TODO: what about Qt or other platforms that may use this? */
1531 return nullptr;
1532 }
1534 gfxFontEntry*
1535 gfxFT2FontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
1536 const uint8_t *aFontData,
1537 uint32_t aLength)
1538 {
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);
1543 }