1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/Logging.h"
7 
8 #include "gfxFcPlatformFontList.h"
9 #include "gfxFont.h"
10 #include "gfxFontConstants.h"
11 #include "gfxFT2Utils.h"
12 #include "gfxPlatform.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/dom/ContentChild.h"
15 #include "mozilla/dom/ContentParent.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/Sprintf.h"
18 #include "mozilla/StaticPrefs_gfx.h"
19 #include "mozilla/Telemetry.h"
20 #include "mozilla/TimeStamp.h"
21 #include "nsGkAtoms.h"
22 #include "nsUnicodeProperties.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 #include "nsCharSeparatedTokenizer.h"
27 #include "nsXULAppAPI.h"
28 #include "SharedFontList-impl.h"
29 #include "StandardFonts-linux.inc"
30 
31 #include "mozilla/gfx/HelpersCairo.h"
32 
33 #include <cairo-ft.h>
34 #include <fontconfig/fcfreetype.h>
35 #include <dlfcn.h>
36 #include <unistd.h>
37 
38 #ifdef MOZ_WIDGET_GTK
39 #  include <gdk/gdk.h>
40 #  include <gtk/gtk.h>
41 #  include "gfxPlatformGtk.h"
42 #  include "mozilla/WidgetUtilsGtk.h"
43 #endif
44 
45 #ifdef MOZ_X11
46 #  include "mozilla/X11Util.h"
47 #endif
48 
49 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
50 #  include "mozilla/SandboxBrokerPolicyFactory.h"
51 #  include "mozilla/SandboxSettings.h"
52 #endif
53 
54 #include FT_MULTIPLE_MASTERS_H
55 
56 using namespace mozilla;
57 using namespace mozilla::gfx;
58 using namespace mozilla::unicode;
59 
60 #ifndef FC_POSTSCRIPT_NAME
61 #  define FC_POSTSCRIPT_NAME "postscriptname" /* String */
62 #endif
63 #ifndef FC_VARIABLE
64 #  define FC_VARIABLE "variable" /* Bool */
65 #endif
66 
67 #define PRINTING_FC_PROPERTY "gfx.printing"
68 
69 #define LOG_FONTLIST(args) \
70   MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
71 #define LOG_FONTLIST_ENABLED() \
72   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
73 #define LOG_CMAPDATA_ENABLED() \
74   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
75 
ToFcChar8Ptr(const char * aStr)76 static const FcChar8* ToFcChar8Ptr(const char* aStr) {
77   return reinterpret_cast<const FcChar8*>(aStr);
78 }
79 
ToCharPtr(const FcChar8 * aStr)80 static const char* ToCharPtr(const FcChar8* aStr) {
81   return reinterpret_cast<const char*>(aStr);
82 }
83 
84 // canonical name ==> first en name or first name if no en name
85 // This is the required logic for fullname lookups as per CSS3 Fonts spec.
FindCanonicalNameIndex(FcPattern * aFont,const char * aLangField)86 static uint32_t FindCanonicalNameIndex(FcPattern* aFont,
87                                        const char* aLangField) {
88   uint32_t n = 0, en = 0;
89   FcChar8* lang;
90   while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
91     // look for 'en' or variants, en-US, en-JP etc.
92     uint32_t len = strlen(ToCharPtr(lang));
93     bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
94     if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
95       en = n;
96       break;
97     }
98     n++;
99   }
100   return en;
101 }
102 
GetFaceNames(FcPattern * aFont,const nsACString & aFamilyName,nsACString & aPostscriptName,nsACString & aFullname)103 static void GetFaceNames(FcPattern* aFont, const nsACString& aFamilyName,
104                          nsACString& aPostscriptName, nsACString& aFullname) {
105   // get the Postscript name
106   FcChar8* psname;
107   if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) ==
108       FcResultMatch) {
109     aPostscriptName = ToCharPtr(psname);
110   }
111 
112   // get the canonical fullname (i.e. en name or first name)
113   uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
114   FcChar8* fullname;
115   if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
116     aFullname = ToCharPtr(fullname);
117   }
118 
119   // if have fullname, done
120   if (!aFullname.IsEmpty()) {
121     return;
122   }
123 
124   // otherwise, set the fullname to family + style name [en] and use that
125   aFullname = aFamilyName;
126 
127   // figure out the en style name
128   en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
129   nsAutoCString style;
130   FcChar8* stylename = nullptr;
131   FcPatternGetString(aFont, FC_STYLE, en, &stylename);
132   if (stylename) {
133     style = ToCharPtr(stylename);
134   }
135 
136   if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
137     aFullname.Append(' ');
138     aFullname.Append(style);
139   }
140 }
141 
MapFcWeight(int aFcWeight)142 static FontWeight MapFcWeight(int aFcWeight) {
143   if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
144     return FontWeight(100);
145   }
146   if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
147     return FontWeight(200);
148   }
149   if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
150     return FontWeight(300);
151   }
152   if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
153     // This includes FC_WEIGHT_BOOK
154     return FontWeight(400);
155   }
156   if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
157     return FontWeight(500);
158   }
159   if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
160     return FontWeight(600);
161   }
162   if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
163     return FontWeight(700);
164   }
165   if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
166     return FontWeight(800);
167   }
168   if (aFcWeight <= FC_WEIGHT_BLACK) {
169     return FontWeight(900);
170   }
171 
172   // including FC_WEIGHT_EXTRABLACK
173   return FontWeight(901);
174 }
175 
176 // TODO(emilio, jfkthame): I think this can now be more fine-grained.
MapFcWidth(int aFcWidth)177 static FontStretch MapFcWidth(int aFcWidth) {
178   if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
179     return FontStretch::UltraCondensed();
180   }
181   if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
182     return FontStretch::ExtraCondensed();
183   }
184   if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
185     return FontStretch::Condensed();
186   }
187   if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
188     return FontStretch::SemiCondensed();
189   }
190   if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
191     return FontStretch::Normal();
192   }
193   if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
194     return FontStretch::SemiExpanded();
195   }
196   if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
197     return FontStretch::Expanded();
198   }
199   if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
200     return FontStretch::ExtraExpanded();
201   }
202   return FontStretch::UltraExpanded();
203 }
204 
GetFontProperties(FcPattern * aFontPattern,WeightRange * aWeight,StretchRange * aStretch,SlantStyleRange * aSlantStyle,uint16_t * aSize=nullptr)205 static void GetFontProperties(FcPattern* aFontPattern, WeightRange* aWeight,
206                               StretchRange* aStretch,
207                               SlantStyleRange* aSlantStyle,
208                               uint16_t* aSize = nullptr) {
209   // weight
210   int weight;
211   if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
212       FcResultMatch) {
213     weight = FC_WEIGHT_REGULAR;
214   }
215   *aWeight = WeightRange(MapFcWeight(weight));
216 
217   // width
218   int width;
219   if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
220     width = FC_WIDTH_NORMAL;
221   }
222   *aStretch = StretchRange(MapFcWidth(width));
223 
224   // italic
225   int slant;
226   if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
227     slant = FC_SLANT_ROMAN;
228   }
229   if (slant == FC_SLANT_OBLIQUE) {
230     *aSlantStyle = SlantStyleRange(FontSlantStyle::Oblique());
231   } else if (slant > 0) {
232     *aSlantStyle = SlantStyleRange(FontSlantStyle::Italic());
233   }
234 
235   if (aSize) {
236     // pixel size, or zero if scalable
237     FcBool scalable;
238     if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
239             FcResultMatch &&
240         scalable) {
241       *aSize = 0;
242     } else {
243       double size;
244       if (FcPatternGetDouble(aFontPattern, FC_PIXEL_SIZE, 0, &size) ==
245           FcResultMatch) {
246         *aSize = uint16_t(NS_round(size));
247       } else {
248         *aSize = 0;
249       }
250     }
251   }
252 }
253 
gfxFontconfigFontEntry(const nsACString & aFaceName,FcPattern * aFontPattern,bool aIgnoreFcCharmap)254 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
255                                                FcPattern* aFontPattern,
256                                                bool aIgnoreFcCharmap)
257     : gfxFT2FontEntryBase(aFaceName),
258       mFontPattern(aFontPattern),
259       mFTFaceInitialized(false),
260       mIgnoreFcCharmap(aIgnoreFcCharmap),
261       mHasVariationsInitialized(false) {
262   GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
263 }
264 
Clone() const265 gfxFontEntry* gfxFontconfigFontEntry::Clone() const {
266   MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
267   return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
268 }
269 
CreatePatternForFace(FT_Face aFace)270 static already_AddRefed<FcPattern> CreatePatternForFace(FT_Face aFace) {
271   // Use fontconfig to fill out the pattern from the FTFace.
272   // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
273   // least). The dummy file passed here is removed below.
274   //
275   // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
276   // is passed as the "blanks" argument, which provides that unexpectedly
277   // blank glyphs are elided.  Here, however, we pass nullptr for
278   // "blanks", effectively assuming that, if the font has a blank glyph,
279   // then the author intends any associated character to be rendered
280   // blank.
281   RefPtr<FcPattern> pattern =
282       dont_AddRef(FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr));
283   // given that we have a FT_Face, not really sure this is possible...
284   if (!pattern) {
285     pattern = dont_AddRef(FcPatternCreate());
286   }
287   FcPatternDel(pattern, FC_FILE);
288   FcPatternDel(pattern, FC_INDEX);
289 
290   // Make a new pattern and store the face in it so that cairo uses
291   // that when creating a cairo font face.
292   FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
293 
294   return pattern.forget();
295 }
296 
CreateFaceForPattern(FcPattern * aPattern)297 static already_AddRefed<SharedFTFace> CreateFaceForPattern(
298     FcPattern* aPattern) {
299   FcChar8* filename;
300   if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
301     return nullptr;
302   }
303   int index;
304   if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
305     index = 0;  // default to 0 if not found in pattern
306   }
307   return Factory::NewSharedFTFace(nullptr, ToCharPtr(filename), index);
308 }
309 
gfxFontconfigFontEntry(const nsACString & aFaceName,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,RefPtr<SharedFTFace> && aFace)310 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
311                                                WeightRange aWeight,
312                                                StretchRange aStretch,
313                                                SlantStyleRange aStyle,
314                                                RefPtr<SharedFTFace>&& aFace)
315     : gfxFT2FontEntryBase(aFaceName),
316       mFTFace(std::move(aFace)),
317       mFTFaceInitialized(true),
318       mIgnoreFcCharmap(true),
319       mHasVariationsInitialized(false) {
320   mWeightRange = aWeight;
321   mStyleRange = aStyle;
322   mStretchRange = aStretch;
323   mIsDataUserFont = true;
324 
325   mFontPattern = CreatePatternForFace(mFTFace->GetFace());
326 }
327 
gfxFontconfigFontEntry(const nsACString & aFaceName,FcPattern * aFontPattern,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle)328 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
329                                                FcPattern* aFontPattern,
330                                                WeightRange aWeight,
331                                                StretchRange aStretch,
332                                                SlantStyleRange aStyle)
333     : gfxFT2FontEntryBase(aFaceName),
334       mFontPattern(aFontPattern),
335       mFTFaceInitialized(false),
336       mHasVariationsInitialized(false) {
337   mWeightRange = aWeight;
338   mStyleRange = aStyle;
339   mStretchRange = aStretch;
340   mIsLocalUserFont = true;
341 
342   // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
343   // via src:local()...
344   // If the local font happens to come from the application fontset,
345   // we want to set it to true so that color/svg fonts will work even
346   // if the default glyphs are blank; but if the local font is a non-
347   // sfnt face (e.g. legacy type 1) then we need to set it to false
348   // because our cmap-reading code will fail and we depend on FT+Fc to
349   // determine the coverage.
350   // We set the flag here, but may flip it the first time TestCharacterMap
351   // is called, at which point we'll look to see whether a 'cmap' is
352   // actually present in the font.
353   mIgnoreFcCharmap = true;
354 }
355 
356 typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
357 typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
358 static GetVarFunc sGetVar;
359 static DoneVarFunc sDoneVar;
360 static bool sInitializedVarFuncs = false;
361 
InitializeVarFuncs()362 static void InitializeVarFuncs() {
363   if (sInitializedVarFuncs) {
364     return;
365   }
366   sInitializedVarFuncs = true;
367 #if MOZ_TREE_FREETYPE
368   sGetVar = &FT_Get_MM_Var;
369   sDoneVar = &FT_Done_MM_Var;
370 #else
371   sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
372   sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
373 #endif
374 }
375 
~gfxFontconfigFontEntry()376 gfxFontconfigFontEntry::~gfxFontconfigFontEntry() {
377   if (mMMVar) {
378     // Prior to freetype 2.9, there was no specific function to free the
379     // FT_MM_Var record, and the docs just said to use free().
380     // InitializeVarFuncs must have been called in order for mMMVar to be
381     // non-null here, so we don't need to do it again.
382     if (sDoneVar) {
383       MOZ_ASSERT(mFTFace, "How did mMMVar get set without a face?");
384       (*sDoneVar)(mFTFace->GetFace()->glyph->library, mMMVar);
385     } else {
386       free(mMMVar);
387     }
388   }
389 }
390 
ReadCMAP(FontInfoData * aFontInfoData)391 nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
392   // attempt this once, if errors occur leave a blank cmap
393   if (mCharacterMap) {
394     return NS_OK;
395   }
396 
397   RefPtr<gfxCharacterMap> charmap;
398   nsresult rv;
399 
400   if (aFontInfoData &&
401       (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
402     rv = NS_OK;
403   } else {
404     uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
405     charmap = new gfxCharacterMap();
406     AutoTable cmapTable(this, kCMAP);
407 
408     if (cmapTable) {
409       uint32_t cmapLen;
410       const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
411           hb_blob_get_data(cmapTable, &cmapLen));
412       rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, mUVSOffset);
413     } else {
414       rv = NS_ERROR_NOT_AVAILABLE;
415     }
416   }
417 
418   mHasCmapTable = NS_SUCCEEDED(rv);
419   if (mHasCmapTable) {
420     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
421     fontlist::FontList* sharedFontList = pfl->SharedFontList();
422     if (!IsUserFont() && mShmemFace) {
423       mShmemFace->SetCharacterMap(sharedFontList, charmap);  // async
424       if (!TrySetShmemCharacterMap()) {
425         // Temporarily retain charmap, until the shared version is
426         // ready for use.
427         mCharacterMap = charmap;
428       }
429     } else {
430       mCharacterMap = pfl->FindCharMap(charmap);
431     }
432   } else {
433     // if error occurred, initialize to null cmap
434     mCharacterMap = new gfxCharacterMap();
435   }
436 
437   LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
438                 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
439                 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
440   if (LOG_CMAPDATA_ENABLED()) {
441     char prefix[256];
442     SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
443     charmap->Dump(prefix, eGfxLog_cmapdata);
444   }
445 
446   return rv;
447 }
448 
HasChar(FcPattern * aFont,FcChar32 aCh)449 static bool HasChar(FcPattern* aFont, FcChar32 aCh) {
450   FcCharSet* charset = nullptr;
451   FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
452   return charset && FcCharSetHasChar(charset, aCh);
453 }
454 
TestCharacterMap(uint32_t aCh)455 bool gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh) {
456   // For user fonts, or for fonts bundled with the app (which might include
457   // color/svg glyphs where the default glyphs may be blank, and thus confuse
458   // fontconfig/freetype's char map checking), we instead check the cmap
459   // directly for character coverage.
460   if (mIgnoreFcCharmap) {
461     // If it does not actually have a cmap, switch our strategy to use
462     // fontconfig's charmap after all (except for data fonts, which must
463     // always have a cmap to have passed OTS validation).
464     if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'))) {
465       mIgnoreFcCharmap = false;
466       // ...and continue with HasChar() below.
467     } else {
468       return gfxFontEntry::TestCharacterMap(aCh);
469     }
470   }
471   // otherwise (for system fonts), use the charmap in the pattern
472   return HasChar(mFontPattern, aCh);
473 }
474 
HasFontTable(uint32_t aTableTag)475 bool gfxFontconfigFontEntry::HasFontTable(uint32_t aTableTag) {
476   if (FTUserFontData* ufd = GetUserFontData()) {
477     return !!gfxFontUtils::FindTableDirEntry(ufd->FontData(), aTableTag);
478   }
479   return gfxFT2FontEntryBase::FaceHasTable(GetFTFace(), aTableTag);
480 }
481 
GetFontTable(uint32_t aTableTag)482 hb_blob_t* gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) {
483   // for data fonts, read directly from the font data
484   if (FTUserFontData* ufd = GetUserFontData()) {
485     return gfxFontUtils::GetTableFromFontData(ufd->FontData(), aTableTag);
486   }
487 
488   return gfxFontEntry::GetFontTable(aTableTag);
489 }
490 
GetAspect(uint8_t aSizeAdjustBasis)491 double gfxFontconfigFontEntry::GetAspect(uint8_t aSizeAdjustBasis) {
492   using FontSizeAdjust = gfxFont::FontSizeAdjust;
493   if (FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::ExHeight ||
494       FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::CapHeight) {
495     // try to compute aspect from OS/2 metrics if available
496     AutoTable os2Table(this, TRUETYPE_TAG('O', 'S', '/', '2'));
497     if (os2Table) {
498       uint16_t upem = UnitsPerEm();
499       if (upem != kInvalidUPEM) {
500         uint32_t len;
501         const auto* os2 =
502             reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
503         if (uint16_t(os2->version) >= 2) {
504           // XXX(jfkthame) Other implementations don't have the check for
505           // values <= 0.1em; should we drop that here? Just require it to be
506           // a positive number?
507           if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
508               FontSizeAdjust::Tag::ExHeight) {
509             if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
510                 int16_t(os2->sxHeight) > 0.1 * upem) {
511               return double(int16_t(os2->sxHeight)) / upem;
512             }
513           }
514           if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
515               FontSizeAdjust::Tag::CapHeight) {
516             if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
517                 int16_t(os2->sCapHeight) > 0.1 * upem) {
518               return double(int16_t(os2->sCapHeight)) / upem;
519             }
520           }
521         }
522       }
523     }
524   }
525 
526   // create a font to calculate the requested aspect
527   gfxFontStyle s;
528   s.size = 256.0;  // pick large size to reduce hinting artifacts
529   RefPtr<gfxFont> font = FindOrMakeFont(&s);
530   if (font) {
531     const gfxFont::Metrics& metrics =
532         font->GetMetrics(nsFontMetrics::eHorizontal);
533     if (metrics.emHeight == 0) {
534       return 0;
535     }
536     switch (FontSizeAdjust::Tag(aSizeAdjustBasis)) {
537       case FontSizeAdjust::Tag::ExHeight:
538         return metrics.xHeight / metrics.emHeight;
539       case FontSizeAdjust::Tag::CapHeight:
540         return metrics.capHeight / metrics.emHeight;
541       case FontSizeAdjust::Tag::ChWidth:
542         return metrics.zeroWidth > 0 ? metrics.zeroWidth / metrics.emHeight
543                                      : 0.5;
544       case FontSizeAdjust::Tag::IcWidth:
545       case FontSizeAdjust::Tag::IcHeight: {
546         bool vertical = FontSizeAdjust::Tag(aSizeAdjustBasis) ==
547                         FontSizeAdjust::Tag::IcHeight;
548         gfxFloat advance = font->GetCharAdvance(0x6C34, vertical);
549         return advance > 0 ? advance / metrics.emHeight : 1.0;
550       }
551       default:
552         break;
553     }
554   }
555 
556   MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
557   return 0.5;
558 }
559 
PrepareFontOptions(FcPattern * aPattern,int * aOutLoadFlags,unsigned int * aOutSynthFlags)560 static void PrepareFontOptions(FcPattern* aPattern, int* aOutLoadFlags,
561                                unsigned int* aOutSynthFlags) {
562   int loadFlags = FT_LOAD_DEFAULT;
563   unsigned int synthFlags = 0;
564 
565   // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
566 
567   FcBool printing;
568   if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
569       FcResultMatch) {
570     printing = FcFalse;
571   }
572 
573   // Font options are set explicitly here to improve cairo's caching
574   // behavior and to record the relevant parts of the pattern so that
575   // the pattern can be released.
576   //
577   // Most font_options have already been set as defaults on the FcPattern
578   // with cairo_ft_font_options_substitute(), then user and system
579   // fontconfig configurations were applied.  The resulting font_options
580   // have been recorded on the face during
581   // cairo_ft_font_face_create_for_pattern().
582   //
583   // None of the settings here cause this scaled_font to behave any
584   // differently from how it would behave if it were created from the same
585   // face with default font_options.
586   //
587   // We set options explicitly so that the same scaled_font will be found in
588   // the cairo_scaled_font_map when cairo loads glyphs from a context with
589   // the same font_face, font_matrix, ctm, and surface font_options.
590   //
591   // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
592   // font_options on the cairo_ft_font_face, and doesn't consider default
593   // option values to not match any explicit values.
594   //
595   // Even after cairo_set_scaled_font is used to set font_options for the
596   // cairo context, when cairo looks for a scaled_font for the context, it
597   // will look for a font with some option values from the target surface if
598   // any values are left default on the context font_options.  If this
599   // scaled_font is created with default font_options, cairo will not find
600   // it.
601   //
602   // The one option not recorded in the pattern is hint_metrics, which will
603   // affect glyph metrics.  The default behaves as CAIRO_HINT_METRICS_ON.
604   // We should be considering the font_options of the surface on which this
605   // font will be used, but currently we don't have different gfxFonts for
606   // different surface font_options, so we'll create a font suitable for the
607   // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
608 
609   // The remaining options have been recorded on the pattern and the face.
610   // _cairo_ft_options_merge has some logic to decide which options from the
611   // scaled_font or from the cairo_ft_font_face take priority in the way the
612   // font behaves.
613   //
614   // In the majority of cases, _cairo_ft_options_merge uses the options from
615   // the cairo_ft_font_face, so sometimes it is not so important which
616   // values are set here so long as they are not defaults, but we'll set
617   // them to the exact values that we expect from the font, to be consistent
618   // and to protect against changes in cairo.
619   //
620   // In some cases, _cairo_ft_options_merge uses some options from the
621   // scaled_font's font_options rather than options on the
622   // cairo_ft_font_face (from fontconfig).
623   // https://bugs.freedesktop.org/show_bug.cgi?id=11838
624   //
625   // Surface font options were set on the pattern in
626   // cairo_ft_font_options_substitute.  If fontconfig has changed the
627   // hint_style then that is what the user (or distribution) wants, so we
628   // use the setting from the FcPattern.
629   //
630   // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
631   FcBool hinting = FcFalse;
632   if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
633     hinting = FcTrue;
634   }
635 
636   int fc_hintstyle = FC_HINT_NONE;
637   if (!printing && hinting &&
638       FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &fc_hintstyle) !=
639           FcResultMatch) {
640     fc_hintstyle = FC_HINT_FULL;
641   }
642   switch (fc_hintstyle) {
643     case FC_HINT_NONE:
644       loadFlags = FT_LOAD_NO_HINTING;
645       break;
646     case FC_HINT_SLIGHT:
647       loadFlags = FT_LOAD_TARGET_LIGHT;
648       break;
649   }
650 
651   FcBool fc_antialias;
652   if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &fc_antialias) !=
653       FcResultMatch) {
654     fc_antialias = FcTrue;
655   }
656   if (!fc_antialias) {
657     if (fc_hintstyle != FC_HINT_NONE) {
658       loadFlags = FT_LOAD_TARGET_MONO;
659     }
660     loadFlags |= FT_LOAD_MONOCHROME;
661   } else if (fc_hintstyle == FC_HINT_FULL) {
662     int fc_rgba;
663     if (FcPatternGetInteger(aPattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch) {
664       fc_rgba = FC_RGBA_UNKNOWN;
665     }
666     switch (fc_rgba) {
667       case FC_RGBA_RGB:
668       case FC_RGBA_BGR:
669         loadFlags = FT_LOAD_TARGET_LCD;
670         break;
671       case FC_RGBA_VRGB:
672       case FC_RGBA_VBGR:
673         loadFlags = FT_LOAD_TARGET_LCD_V;
674         break;
675     }
676   }
677 
678   FcBool bitmap;
679   if (FcPatternGetBool(aPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) !=
680       FcResultMatch) {
681     bitmap = FcFalse;
682   }
683   if (fc_antialias && (fc_hintstyle == FC_HINT_NONE || !bitmap)) {
684     loadFlags |= FT_LOAD_NO_BITMAP;
685   }
686 
687   FcBool autohint;
688   if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
689       autohint) {
690     loadFlags |= FT_LOAD_FORCE_AUTOHINT;
691   }
692 
693   FcBool embolden;
694   if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
695       embolden) {
696     synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
697   }
698 
699   *aOutLoadFlags = loadFlags;
700   *aOutSynthFlags = synthFlags;
701 }
702 
703 #ifdef MOZ_X11
GetXftInt(Display * aDisplay,const char * aName,int * aResult)704 static bool GetXftInt(Display* aDisplay, const char* aName, int* aResult) {
705   if (!aDisplay) {
706     return false;
707   }
708   char* value = XGetDefault(aDisplay, "Xft", aName);
709   if (!value) {
710     return false;
711   }
712   if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
713     return true;
714   }
715   char* end;
716   *aResult = strtol(value, &end, 0);
717   if (end != value) {
718     return true;
719   }
720   return false;
721 }
722 #endif
723 
PreparePattern(FcPattern * aPattern,bool aIsPrinterFont)724 static void PreparePattern(FcPattern* aPattern, bool aIsPrinterFont) {
725   FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
726 
727   // This gets cairo_font_options_t for the Screen.  We should have
728   // different font options for printing (no hinting) but we are not told
729   // what we are measuring for.
730   //
731   // If cairo adds support for lcd_filter, gdk will not provide the default
732   // setting for that option.  We could get the default setting by creating
733   // an xlib surface once, recording its font_options, and then merging the
734   // gdk options.
735   //
736   // Using an xlib surface would also be an option to get Screen font
737   // options for non-GTK X11 toolkits, but less efficient than using GDK to
738   // pick up dynamic changes.
739   if (aIsPrinterFont) {
740     cairo_font_options_t* options = cairo_font_options_create();
741     cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
742     cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
743     cairo_ft_font_options_substitute(options, aPattern);
744     cairo_font_options_destroy(options);
745     FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
746 #ifdef MOZ_WIDGET_GTK
747   } else {
748     gfxFcPlatformFontList::PlatformFontList()->SubstituteSystemFontOptions(
749         aPattern);
750 #endif  // MOZ_WIDGET_GTK
751   }
752 
753   FcDefaultSubstitute(aPattern);
754 }
755 
MoveToFront(size_t aIndex)756 void gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
757   if (aIndex > 0) {
758     ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
759         std::move(mUnscaledFonts[aIndex]);
760     for (size_t i = aIndex; i > 0; i--) {
761       mUnscaledFonts[i] = std::move(mUnscaledFonts[i - 1]);
762     }
763     mUnscaledFonts[0] = std::move(front);
764   }
765 }
766 
767 already_AddRefed<UnscaledFontFontconfig>
Lookup(const std::string & aFile,uint32_t aIndex)768 gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const std::string& aFile,
769                                                   uint32_t aIndex) {
770   for (size_t i = 0; i < kNumEntries; i++) {
771     RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
772     if (entry && entry->GetFile() == aFile && entry->GetIndex() == aIndex) {
773       MoveToFront(i);
774       return entry.forget();
775     }
776   }
777   return nullptr;
778 }
779 
SizeForStyle(gfxFontconfigFontEntry * aEntry,const gfxFontStyle & aStyle)780 static inline gfxFloat SizeForStyle(gfxFontconfigFontEntry* aEntry,
781                                     const gfxFontStyle& aStyle) {
782   return StyleFontSizeAdjust::Tag(aStyle.sizeAdjustBasis) !=
783                  StyleFontSizeAdjust::Tag::None
784              ? aStyle.GetAdjustedSize(aEntry->GetAspect(aStyle.sizeAdjustBasis))
785              : aStyle.size * aEntry->mSizeAdjust;
786 }
787 
ChooseFontSize(gfxFontconfigFontEntry * aEntry,const gfxFontStyle & aStyle)788 static double ChooseFontSize(gfxFontconfigFontEntry* aEntry,
789                              const gfxFontStyle& aStyle) {
790   double requestedSize = SizeForStyle(aEntry, aStyle);
791   double bestDist = -1.0;
792   double bestSize = requestedSize;
793   double size;
794   int v = 0;
795   while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
796          FcResultMatch) {
797     ++v;
798     double dist = fabs(size - requestedSize);
799     if (bestDist < 0.0 || dist < bestDist) {
800       bestDist = dist;
801       bestSize = size;
802     }
803   }
804   // If the font has bitmaps but wants to be scaled, then let it scale.
805   if (bestSize >= 0.0) {
806     FcBool scalable;
807     if (FcPatternGetBool(aEntry->GetPattern(), FC_SCALABLE, 0, &scalable) ==
808             FcResultMatch &&
809         scalable) {
810       return requestedSize;
811     }
812   }
813   return bestSize;
814 }
815 
CreateFontInstance(const gfxFontStyle * aFontStyle)816 gfxFont* gfxFontconfigFontEntry::CreateFontInstance(
817     const gfxFontStyle* aFontStyle) {
818   RefPtr<FcPattern> pattern = dont_AddRef(FcPatternCreate());
819   if (!pattern) {
820     NS_WARNING("Failed to create Fontconfig pattern for font instance");
821     return nullptr;
822   }
823 
824   double size = ChooseFontSize(this, *aFontStyle);
825   FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
826 
827   RefPtr<SharedFTFace> face = GetFTFace();
828   if (!face) {
829     NS_WARNING("Failed to get FreeType face for pattern");
830     return nullptr;
831   }
832   if (HasVariations()) {
833     // For variation fonts, we create a new FT_Face here so that
834     // variation coordinates from the style can be applied without
835     // affecting other font instances created from the same entry
836     // (font resource).
837     // For user fonts: create a new FT_Face from the font data, and then make
838     // a pattern from that.
839     // For system fonts: create a new FT_Face and store it in a copy of the
840     // original mFontPattern.
841     RefPtr<SharedFTFace> varFace = face->GetData()
842                                        ? face->GetData()->CloneFace()
843                                        : CreateFaceForPattern(mFontPattern);
844     if (varFace) {
845       AutoTArray<gfxFontVariation, 8> settings;
846       GetVariationsForStyle(settings, *aFontStyle);
847       gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, varFace->GetFace());
848       face = std::move(varFace);
849     }
850   }
851 
852   PreparePattern(pattern, aFontStyle->printerFont);
853   RefPtr<FcPattern> renderPattern =
854       dont_AddRef(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
855   if (!renderPattern) {
856     NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
857     return nullptr;
858   }
859 
860   if (aFontStyle->NeedsSyntheticBold(this)) {
861     FcPatternAddBool(renderPattern, FC_EMBOLDEN, FcTrue);
862   }
863 
864   // will synthetic oblique be applied using a transform?
865   if (IsUpright() && aFontStyle->style != FontSlantStyle::Normal() &&
866       aFontStyle->allowSyntheticStyle) {
867     // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
868     FcPatternDel(renderPattern, FC_EMBEDDED_BITMAP);
869     FcPatternAddBool(renderPattern, FC_EMBEDDED_BITMAP, FcFalse);
870   }
871 
872   int loadFlags;
873   unsigned int synthFlags;
874   PrepareFontOptions(renderPattern, &loadFlags, &synthFlags);
875 
876   std::string file;
877   int index = 0;
878   if (!face->GetData()) {
879     const FcChar8* fcFile;
880     if (FcPatternGetString(renderPattern, FC_FILE, 0,
881                            const_cast<FcChar8**>(&fcFile)) != FcResultMatch ||
882         FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) !=
883             FcResultMatch) {
884       NS_WARNING("No file in Fontconfig pattern for font instance");
885       return nullptr;
886     }
887     file = ToCharPtr(fcFile);
888   }
889 
890   RefPtr<UnscaledFontFontconfig> unscaledFont =
891       mUnscaledFontCache.Lookup(file, index);
892   if (!unscaledFont) {
893     unscaledFont = mFTFace->GetData() ? new UnscaledFontFontconfig(mFTFace)
894                                       : new UnscaledFontFontconfig(
895                                             std::move(file), index, mFTFace);
896     mUnscaledFontCache.Add(unscaledFont);
897   }
898 
899   gfxFont* newFont = new gfxFontconfigFont(
900       unscaledFont, std::move(face), renderPattern, size, this, aFontStyle,
901       loadFlags, (synthFlags & CAIRO_FT_SYNTHESIZE_BOLD) != 0);
902 
903   return newFont;
904 }
905 
GetFTFace()906 const RefPtr<SharedFTFace>& gfxFontconfigFontEntry::GetFTFace() {
907   if (!mFTFaceInitialized) {
908     mFTFaceInitialized = true;
909     mFTFace = CreateFaceForPattern(mFontPattern);
910   }
911   return mFTFace;
912 }
913 
GetUserFontData()914 FTUserFontData* gfxFontconfigFontEntry::GetUserFontData() {
915   if (mFTFace && mFTFace->GetData()) {
916     return static_cast<FTUserFontData*>(mFTFace->GetData());
917   }
918   return nullptr;
919 }
920 
HasVariations()921 bool gfxFontconfigFontEntry::HasVariations() {
922   if (mHasVariationsInitialized) {
923     return mHasVariations;
924   }
925   mHasVariationsInitialized = true;
926   mHasVariations = false;
927 
928   if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
929     return mHasVariations;
930   }
931 
932   // For installed fonts, query the fontconfig pattern rather than paying
933   // the cost of loading a FT_Face that we otherwise might never need.
934   if (!IsUserFont() || IsLocalUserFont()) {
935     FcBool variable;
936     if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0, &variable) ==
937          FcResultMatch) &&
938         variable) {
939       mHasVariations = true;
940     }
941   } else {
942     if (GetFTFace()) {
943       mHasVariations =
944           mFTFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS;
945     }
946   }
947 
948   return mHasVariations;
949 }
950 
GetMMVar()951 FT_MM_Var* gfxFontconfigFontEntry::GetMMVar() {
952   if (mMMVarInitialized) {
953     return mMMVar;
954   }
955   mMMVarInitialized = true;
956   InitializeVarFuncs();
957   if (!sGetVar) {
958     return nullptr;
959   }
960   if (!GetFTFace()) {
961     return nullptr;
962   }
963   if (FT_Err_Ok != (*sGetVar)(mFTFace->GetFace(), &mMMVar)) {
964     mMMVar = nullptr;
965   }
966   return mMMVar;
967 }
968 
GetVariationAxes(nsTArray<gfxFontVariationAxis> & aAxes)969 void gfxFontconfigFontEntry::GetVariationAxes(
970     nsTArray<gfxFontVariationAxis>& aAxes) {
971   if (!HasVariations()) {
972     return;
973   }
974   gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes);
975 }
976 
GetVariationInstances(nsTArray<gfxFontVariationInstance> & aInstances)977 void gfxFontconfigFontEntry::GetVariationInstances(
978     nsTArray<gfxFontVariationInstance>& aInstances) {
979   if (!HasVariations()) {
980     return;
981   }
982   gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances);
983 }
984 
CopyFontTable(uint32_t aTableTag,nsTArray<uint8_t> & aBuffer)985 nsresult gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
986                                                nsTArray<uint8_t>& aBuffer) {
987   NS_ASSERTION(!mIsDataUserFont,
988                "data fonts should be reading tables directly from memory");
989   return gfxFT2FontEntryBase::CopyFaceTable(GetFTFace(), aTableTag, aBuffer);
990 }
991 
FindStyleVariations(FontInfoData * aFontInfoData)992 void gfxFontconfigFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
993   if (mHasStyles) {
994     return;
995   }
996 
997   // add font entries for each of the faces
998   uint32_t numFonts = mFontPatterns.Length();
999   NS_ASSERTION(numFonts, "font family containing no faces!!");
1000   uint32_t numRegularFaces = 0;
1001   for (uint32_t i = 0; i < numFonts; i++) {
1002     FcPattern* face = mFontPatterns[i];
1003 
1004     // figure out the psname/fullname and choose which to use as the facename
1005     nsAutoCString psname, fullname;
1006     GetFaceNames(face, mName, psname, fullname);
1007     const nsAutoCString& faceName = !psname.IsEmpty() ? psname : fullname;
1008 
1009     gfxFontconfigFontEntry* fontEntry =
1010         new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
1011 
1012     if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
1013       fontEntry->SetupVariationRanges();
1014     }
1015 
1016     AddFontEntry(fontEntry);
1017 
1018     if (fontEntry->IsNormalStyle()) {
1019       numRegularFaces++;
1020     }
1021 
1022     if (LOG_FONTLIST_ENABLED()) {
1023       nsAutoCString weightString;
1024       fontEntry->Weight().ToString(weightString);
1025       nsAutoCString stretchString;
1026       fontEntry->Stretch().ToString(stretchString);
1027       nsAutoCString styleString;
1028       fontEntry->SlantStyle().ToString(styleString);
1029       LOG_FONTLIST(
1030           ("(fontlist) added (%s) to family (%s)"
1031            " with style: %s weight: %s stretch: %s"
1032            " psname: %s fullname: %s",
1033            fontEntry->Name().get(), Name().get(), styleString.get(),
1034            weightString.get(), stretchString.get(), psname.get(),
1035            fullname.get()));
1036     }
1037   }
1038 
1039   // somewhat arbitrary, but define a family with two or more regular
1040   // faces as a family for which intra-family fallback should be used
1041   if (numRegularFaces > 1) {
1042     mCheckForFallbackFaces = true;
1043   }
1044   mFaceNamesInitialized = true;
1045   mFontPatterns.Clear();
1046   SetHasStyles(true);
1047 
1048   CheckForSimpleFamily();
1049 }
1050 
AddFontPattern(FcPattern * aFontPattern,bool aSingleName)1051 void gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern,
1052                                              bool aSingleName) {
1053   NS_ASSERTION(
1054       !mHasStyles,
1055       "font patterns must not be added to already enumerated families");
1056 
1057   FcBool outline;
1058   if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) !=
1059           FcResultMatch ||
1060       !outline) {
1061     mHasNonScalableFaces = true;
1062 
1063     FcBool scalable;
1064     if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
1065             FcResultMatch &&
1066         scalable) {
1067       mForceScalable = true;
1068     }
1069   }
1070 
1071   if (aSingleName) {
1072     mFontPatterns.InsertElementAt(mUniqueNameFaceCount++, aFontPattern);
1073   } else {
1074     mFontPatterns.AppendElement(aFontPattern);
1075   }
1076 }
1077 
1078 static const double kRejectDistance = 10000.0;
1079 
1080 // Calculate a distance score representing the size disparity between the
1081 // requested style's size and the font entry's size.
SizeDistance(gfxFontconfigFontEntry * aEntry,const gfxFontStyle & aStyle,bool aForceScalable)1082 static double SizeDistance(gfxFontconfigFontEntry* aEntry,
1083                            const gfxFontStyle& aStyle, bool aForceScalable) {
1084   double requestedSize = SizeForStyle(aEntry, aStyle);
1085   double bestDist = -1.0;
1086   double size;
1087   int v = 0;
1088   while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
1089          FcResultMatch) {
1090     ++v;
1091     double dist = fabs(size - requestedSize);
1092     if (bestDist < 0.0 || dist < bestDist) {
1093       bestDist = dist;
1094     }
1095   }
1096   if (bestDist < 0.0) {
1097     // No size means scalable
1098     return -1.0;
1099   } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
1100     // fontconfig prefers a matching family or lang to pixelsize of bitmap
1101     // fonts. CSS suggests a tolerance of 20% on pixelsize.
1102     return bestDist;
1103   } else {
1104     // Reject any non-scalable fonts that are not within tolerance.
1105     return kRejectDistance;
1106   }
1107 }
1108 
FindAllFontsForStyle(const gfxFontStyle & aFontStyle,nsTArray<gfxFontEntry * > & aFontEntryList,bool aIgnoreSizeTolerance)1109 void gfxFontconfigFontFamily::FindAllFontsForStyle(
1110     const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
1111     bool aIgnoreSizeTolerance) {
1112   gfxFontFamily::FindAllFontsForStyle(aFontStyle, aFontEntryList,
1113                                       aIgnoreSizeTolerance);
1114 
1115   if (!mHasNonScalableFaces) {
1116     return;
1117   }
1118 
1119   // Iterate over the the available fonts while compacting any groups
1120   // of unscalable fonts with matching styles into a single entry
1121   // corresponding to the closest available size. If the closest
1122   // available size is rejected for being outside tolerance, then the
1123   // entire group will be skipped.
1124   size_t skipped = 0;
1125   gfxFontconfigFontEntry* bestEntry = nullptr;
1126   double bestDist = -1.0;
1127   for (size_t i = 0; i < aFontEntryList.Length(); i++) {
1128     gfxFontconfigFontEntry* entry =
1129         static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
1130     double dist =
1131         SizeDistance(entry, aFontStyle, mForceScalable || aIgnoreSizeTolerance);
1132     // If the entry is scalable or has a style that does not match
1133     // the group of unscalable fonts, then start a new group.
1134     if (dist < 0.0 || !bestEntry || bestEntry->Stretch() != entry->Stretch() ||
1135         bestEntry->Weight() != entry->Weight() ||
1136         bestEntry->SlantStyle() != entry->SlantStyle()) {
1137       // If the best entry in this group is still outside the tolerance,
1138       // then skip the entire group.
1139       if (bestDist >= kRejectDistance) {
1140         skipped++;
1141       }
1142       // Remove any compacted entries from the previous group.
1143       if (skipped) {
1144         i -= skipped;
1145         aFontEntryList.RemoveElementsAt(i, skipped);
1146         skipped = 0;
1147       }
1148       // Mark the start of the new group.
1149       bestEntry = entry;
1150       bestDist = dist;
1151     } else {
1152       // If this entry more closely matches the requested size than the
1153       // current best in the group, then take this entry instead.
1154       if (dist < bestDist) {
1155         aFontEntryList[i - 1 - skipped] = entry;
1156         bestEntry = entry;
1157         bestDist = dist;
1158       }
1159       skipped++;
1160     }
1161   }
1162   // If the best entry in this group is still outside the tolerance,
1163   // then skip the entire group.
1164   if (bestDist >= kRejectDistance) {
1165     skipped++;
1166   }
1167   // Remove any compacted entries from the current group.
1168   if (skipped) {
1169     aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
1170   }
1171 }
1172 
PatternHasLang(const FcPattern * aPattern,const FcChar8 * aLang)1173 static bool PatternHasLang(const FcPattern* aPattern, const FcChar8* aLang) {
1174   FcLangSet* langset;
1175 
1176   if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
1177     return false;
1178   }
1179 
1180   if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
1181     return true;
1182   }
1183   return false;
1184 }
1185 
SupportsLangGroup(nsAtom * aLangGroup) const1186 bool gfxFontconfigFontFamily::SupportsLangGroup(nsAtom* aLangGroup) const {
1187   if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
1188     return true;
1189   }
1190 
1191   nsAutoCString fcLang;
1192   gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1193   pfl->GetSampleLangForGroup(aLangGroup, fcLang);
1194   if (fcLang.IsEmpty()) {
1195     return true;
1196   }
1197 
1198   // Before FindStyleVariations has been called, mFontPatterns will contain
1199   // the font patterns.  Afterward, it'll be empty, but mAvailableFonts
1200   // will contain the font entries, each of which holds a reference to its
1201   // pattern.  We only check the first pattern in each list, because support
1202   // for langs is considered to be consistent across all faces in a family.
1203   FcPattern* fontPattern;
1204   if (mFontPatterns.Length()) {
1205     fontPattern = mFontPatterns[0];
1206   } else if (mAvailableFonts.Length()) {
1207     fontPattern = static_cast<gfxFontconfigFontEntry*>(mAvailableFonts[0].get())
1208                       ->GetPattern();
1209   } else {
1210     return true;
1211   }
1212 
1213   // is lang included in the underlying pattern?
1214   return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
1215 }
1216 
1217 /* virtual */
~gfxFontconfigFontFamily()1218 gfxFontconfigFontFamily::~gfxFontconfigFontFamily() {
1219   // Should not be dropped by stylo
1220   MOZ_ASSERT(NS_IsMainThread());
1221 }
1222 
1223 template <typename Func>
AddFacesToFontList(Func aAddPatternFunc)1224 void gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc) {
1225   if (HasStyles()) {
1226     for (auto& fe : mAvailableFonts) {
1227       if (!fe) {
1228         continue;
1229       }
1230       auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
1231       aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
1232     }
1233   } else {
1234     for (auto& pat : mFontPatterns) {
1235       aAddPatternFunc(pat, mContainsAppFonts);
1236     }
1237   }
1238 }
1239 
gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig> & aUnscaledFont,RefPtr<SharedFTFace> && aFTFace,FcPattern * aPattern,gfxFloat aAdjustedSize,gfxFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,int aLoadFlags,bool aEmbolden)1240 gfxFontconfigFont::gfxFontconfigFont(
1241     const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
1242     RefPtr<SharedFTFace>&& aFTFace, FcPattern* aPattern, gfxFloat aAdjustedSize,
1243     gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle, int aLoadFlags,
1244     bool aEmbolden)
1245     : gfxFT2FontBase(aUnscaledFont, std::move(aFTFace), aFontEntry, aFontStyle,
1246                      aLoadFlags, aEmbolden),
1247       mPattern(aPattern) {
1248   mAdjustedSize = aAdjustedSize;
1249   InitMetrics();
1250 }
1251 
1252 gfxFontconfigFont::~gfxFontconfigFont() = default;
1253 
GetScaledFont(mozilla::gfx::DrawTarget * aTarget)1254 already_AddRefed<ScaledFont> gfxFontconfigFont::GetScaledFont(
1255     mozilla::gfx::DrawTarget* aTarget) {
1256   if (!mAzureScaledFont) {
1257     mAzureScaledFont = Factory::CreateScaledFontForFontconfigFont(
1258         GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
1259     InitializeScaledFont();
1260   }
1261 
1262   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
1263   return scaledFont.forget();
1264 }
1265 
ShouldHintMetrics() const1266 bool gfxFontconfigFont::ShouldHintMetrics() const {
1267   return !GetStyle()->printerFont;
1268 }
1269 
gfxFcPlatformFontList()1270 gfxFcPlatformFontList::gfxFcPlatformFontList()
1271     : mLocalNames(64),
1272       mGenericMappings(32),
1273       mFcSubstituteCache(64),
1274       mLastConfig(nullptr),
1275       mAlwaysUseFontconfigGenerics(true) {
1276   CheckFamilyList(kBaseFonts_Ubuntu_20_04,
1277                   ArrayLength(kBaseFonts_Ubuntu_20_04));
1278   CheckFamilyList(kLangFonts_Ubuntu_20_04,
1279                   ArrayLength(kLangFonts_Ubuntu_20_04));
1280   CheckFamilyList(kBaseFonts_Fedora_32, ArrayLength(kBaseFonts_Fedora_32));
1281   mLastConfig = FcConfigGetCurrent();
1282   if (XRE_IsParentProcess()) {
1283     // if the rescan interval is set, start the timer
1284     int rescanInterval = FcConfigGetRescanInterval(nullptr);
1285     if (rescanInterval) {
1286       NS_NewTimerWithFuncCallback(
1287           getter_AddRefs(mCheckFontUpdatesTimer), CheckFontUpdates, this,
1288           (rescanInterval + 1) * 1000, nsITimer::TYPE_REPEATING_SLACK,
1289           "gfxFcPlatformFontList::gfxFcPlatformFontList");
1290       if (!mCheckFontUpdatesTimer) {
1291         NS_WARNING("Failure to create font updates timer");
1292       }
1293     }
1294   }
1295 
1296 #ifdef MOZ_BUNDLED_FONTS
1297   mBundledFontsInitialized = false;
1298 #endif
1299 }
1300 
~gfxFcPlatformFontList()1301 gfxFcPlatformFontList::~gfxFcPlatformFontList() {
1302   if (mCheckFontUpdatesTimer) {
1303     mCheckFontUpdatesTimer->Cancel();
1304     mCheckFontUpdatesTimer = nullptr;
1305   }
1306 #ifdef MOZ_WIDGET_GTK
1307   ClearSystemFontOptions();
1308 #endif
1309 }
1310 
AddFontSetFamilies(FcFontSet * aFontSet,const SandboxPolicy * aPolicy,bool aAppFonts)1311 void gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
1312                                                const SandboxPolicy* aPolicy,
1313                                                bool aAppFonts) {
1314   // This iterates over the fonts in a font set and adds in gfxFontFamily
1315   // objects for each family. Individual gfxFontEntry objects for each face
1316   // are not created here; the patterns are just stored in the family. When
1317   // a family is actually used, it will be populated with gfxFontEntry
1318   // records and the patterns moved to those.
1319 
1320   if (!aFontSet) {
1321     NS_WARNING("AddFontSetFamilies called with a null font set.");
1322     return;
1323   }
1324 
1325   FcChar8* lastFamilyName = (FcChar8*)"";
1326   RefPtr<gfxFontconfigFontFamily> fontFamily;
1327   nsAutoCString familyName;
1328   for (int f = 0; f < aFontSet->nfont; f++) {
1329     FcPattern* pattern = aFontSet->fonts[f];
1330 
1331     // Skip any fonts that aren't readable for us (e.g. due to restrictive
1332     // file ownership/permissions).
1333     FcChar8* path;
1334     if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1335       continue;
1336     }
1337     if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1338       continue;
1339     }
1340 
1341 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1342     // Skip any fonts that will be blocked by the content-process sandbox
1343     // policy.
1344     if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1345                      SandboxBroker::Perms::MAY_READ)) {
1346       continue;
1347     }
1348 #endif
1349 
1350     AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
1351                          aAppFonts);
1352   }
1353 }
1354 
AddPatternToFontList(FcPattern * aFont,FcChar8 * & aLastFamilyName,nsACString & aFamilyName,RefPtr<gfxFontconfigFontFamily> & aFontFamily,bool aAppFonts)1355 void gfxFcPlatformFontList::AddPatternToFontList(
1356     FcPattern* aFont, FcChar8*& aLastFamilyName, nsACString& aFamilyName,
1357     RefPtr<gfxFontconfigFontFamily>& aFontFamily, bool aAppFonts) {
1358   // get canonical name
1359   uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
1360   FcChar8* canonical = nullptr;
1361   FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
1362   if (!canonical) {
1363     return;
1364   }
1365 
1366   // same as the last one? no need to add a new family, skip
1367   if (FcStrCmp(canonical, aLastFamilyName) != 0) {
1368     aLastFamilyName = canonical;
1369 
1370     // add new family if one doesn't already exist
1371     aFamilyName.Truncate();
1372     aFamilyName = ToCharPtr(canonical);
1373     nsAutoCString keyName(aFamilyName);
1374     ToLowerCase(keyName);
1375 
1376     aFontFamily = static_cast<gfxFontconfigFontFamily*>(
1377         mFontFamilies
1378             .LookupOrInsertWith(keyName,
1379                                 [&] {
1380                                   FontVisibility visibility =
1381                                       aAppFonts
1382                                           ? FontVisibility::Base
1383                                           : GetVisibilityForFamily(keyName);
1384                                   return MakeRefPtr<gfxFontconfigFontFamily>(
1385                                       aFamilyName, visibility);
1386                                 })
1387             .get());
1388     // Record if the family contains fonts from the app font set
1389     // (in which case we won't rely on fontconfig's charmap, due to
1390     // bug 1276594).
1391     if (aAppFonts) {
1392       aFontFamily->SetFamilyContainsAppFonts(true);
1393     }
1394   }
1395 
1396   // Add pointers to other localized family names. Most fonts
1397   // only have a single name, so the first call to GetString
1398   // will usually not match
1399   FcChar8* otherName;
1400   int n = (cIndex == 0 ? 1 : 0);
1401   while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) == FcResultMatch) {
1402     nsAutoCString otherFamilyName(ToCharPtr(otherName));
1403     AddOtherFamilyName(aFontFamily, otherFamilyName);
1404     n++;
1405     if (n == int(cIndex)) {
1406       n++;  // skip over canonical name
1407     }
1408   }
1409 
1410   const bool singleName = n == 1;
1411 
1412   MOZ_ASSERT(aFontFamily, "font must belong to a font family");
1413   aFontFamily->AddFontPattern(aFont, singleName);
1414 
1415   // map the psname, fullname ==> font family for local font lookups
1416   nsAutoCString psname, fullname;
1417   GetFaceNames(aFont, aFamilyName, psname, fullname);
1418   if (!psname.IsEmpty()) {
1419     ToLowerCase(psname);
1420     mLocalNames.InsertOrUpdate(psname, RefPtr{aFont});
1421   }
1422   if (!fullname.IsEmpty()) {
1423     ToLowerCase(fullname);
1424     mLocalNames.WithEntryHandle(fullname, [&](auto&& entry) {
1425       if (entry && !singleName) {
1426         return;
1427       }
1428       entry.InsertOrUpdate(RefPtr{aFont});
1429     });
1430   }
1431 }
1432 
InitFontListForPlatform()1433 nsresult gfxFcPlatformFontList::InitFontListForPlatform() {
1434 #ifdef MOZ_BUNDLED_FONTS
1435   if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1436     ActivateBundledFonts();
1437   }
1438 #endif
1439 
1440   mLocalNames.Clear();
1441   mFcSubstituteCache.Clear();
1442 
1443   ClearSystemFontOptions();
1444 
1445   mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1446   mOtherFamilyNamesInitialized = true;
1447 
1448   mLastConfig = FcConfigGetCurrent();
1449 
1450   if (XRE_IsContentProcess()) {
1451     // Content process: use the font list passed from the chrome process,
1452     // because we can't rely on fontconfig in the presence of sandboxing;
1453     // it may report fonts that we can't actually access.
1454 
1455     FcChar8* lastFamilyName = (FcChar8*)"";
1456     RefPtr<gfxFontconfigFontFamily> fontFamily;
1457     nsAutoCString familyName;
1458 
1459     // Get font list that was passed during XPCOM startup
1460     // or in an UpdateFontList message.
1461     auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1462 
1463 #ifdef MOZ_WIDGET_GTK
1464     UpdateSystemFontOptionsFromIpc(fontList.options());
1465 #endif
1466 
1467     // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
1468     // we need to escape any leading space in the charset element,
1469     // otherwise FcNameParse will fail. :(
1470     //
1471     // The bug was introduced on 2013-05-24 by
1472     //   https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
1473     //   "Bug 64906 - FcNameParse() should ignore leading whitespace in
1474     //   parameters"
1475     // because ignoring a leading space in the encoded value of charset
1476     // causes erroneous decoding of the whole element.
1477     // This first shipped in version 2.10.94, and was eventually fixed as
1478     // a side-effect of switching to the "human-readable" representation of
1479     // charsets on 2014-07-03 in
1480     //   https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
1481     //   "Change charset parse/unparse format to be human readable"
1482     // (with a followup fix next day) which means a leading space is no
1483     // longer significant. This fix landed after 2.11.1 had been shipped,
1484     // so the first version tag without the bug is 2.11.91.
1485     int fcVersion = FcGetVersion();
1486     bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1487 
1488     for (FontPatternListEntry& fpe : fontList.entries()) {
1489       nsCString& patternStr = fpe.pattern();
1490       if (fcCharsetParseBug) {
1491         int32_t index = patternStr.Find(":charset= ");
1492         if (index != kNotFound) {
1493           // insert backslash after the =, before the space
1494           patternStr.Insert('\\', index + 9);
1495         }
1496       }
1497       FcPattern* pattern = FcNameParse((const FcChar8*)patternStr.get());
1498       AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
1499                            fpe.appFontFamily());
1500       FcPatternDestroy(pattern);
1501     }
1502 
1503     LOG_FONTLIST(
1504         ("got font list from chrome process: "
1505          "%u faces in %u families",
1506          (unsigned)fontList.entries().Length(), mFontFamilies.Count()));
1507 
1508     fontList.entries().Clear();
1509     return NS_OK;
1510   }
1511 
1512   UpdateSystemFontOptions();
1513 
1514   UniquePtr<SandboxPolicy> policy;
1515 
1516 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1517   // If read sandboxing is enabled, create a temporary SandboxPolicy to
1518   // check font paths; use a fake PID to avoid picking up any PID-specific
1519   // rules by accident.
1520   SandboxBrokerPolicyFactory policyFactory;
1521   if (GetEffectiveContentSandboxLevel() > 2 &&
1522       !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1523     policy = policyFactory.GetContentPolicy(-1, false);
1524   }
1525 #endif
1526 
1527 #ifdef MOZ_BUNDLED_FONTS
1528   // https://bugzilla.mozilla.org/show_bug.cgi?id=1745715:
1529   // It's important to do this *before* processing the standard system fonts,
1530   // so that if a family is present in both font sets, we'll treat it as app-
1531   // bundled and therefore always visible.
1532   if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1533     FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1534     AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1535   }
1536 #endif
1537 
1538   // iterate over available fonts
1539   FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1540   AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
1541 
1542   return NS_OK;
1543 }
1544 
ReadSystemFontList(dom::SystemFontList * retValue)1545 void gfxFcPlatformFontList::ReadSystemFontList(dom::SystemFontList* retValue) {
1546   // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
1547   // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
1548   // an older version, we manually append it to the unparsed pattern.
1549 #ifdef MOZ_WIDGET_GTK
1550   SystemFontOptionsToIpc(retValue->options());
1551 #endif
1552 
1553   if (FcGetVersion() < 20900) {
1554     for (const auto& entry : mFontFamilies) {
1555       auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
1556       family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1557         char* s = (char*)FcNameUnparse(aPat);
1558         nsDependentCString patternStr(s);
1559         char* file = nullptr;
1560         if (FcResultMatch ==
1561             FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&file)) {
1562           patternStr.Append(":file=");
1563           patternStr.Append(file);
1564         }
1565         retValue->entries().AppendElement(
1566             FontPatternListEntry(patternStr, aAppFonts));
1567         free(s);
1568       });
1569     }
1570   } else {
1571     for (const auto& entry : mFontFamilies) {
1572       auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
1573       family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1574         char* s = (char*)FcNameUnparse(aPat);
1575         nsDependentCString patternStr(s);
1576         retValue->entries().AppendElement(
1577             FontPatternListEntry(patternStr, aAppFonts));
1578         free(s);
1579       });
1580     }
1581   }
1582 }
1583 
1584 // Per family array of faces.
1585 class FacesData {
1586   using FaceInitArray = AutoTArray<fontlist::Face::InitData, 8>;
1587 
1588   FaceInitArray mFaces;
1589 
1590   // Number of faces that have a single name. Faces that have multiple names are
1591   // sorted last.
1592   uint32_t mUniqueNameFaceCount = 0;
1593 
1594  public:
Add(fontlist::Face::InitData && aData,bool aSingleName)1595   void Add(fontlist::Face::InitData&& aData, bool aSingleName) {
1596     if (aSingleName) {
1597       mFaces.InsertElementAt(mUniqueNameFaceCount++, std::move(aData));
1598     } else {
1599       mFaces.AppendElement(std::move(aData));
1600     }
1601   }
1602 
Get() const1603   const FaceInitArray& Get() const { return mFaces; }
1604 };
1605 
InitSharedFontListForPlatform()1606 void gfxFcPlatformFontList::InitSharedFontListForPlatform() {
1607   mLocalNames.Clear();
1608   mFcSubstituteCache.Clear();
1609 
1610   mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1611   mOtherFamilyNamesInitialized = true;
1612 
1613   mLastConfig = FcConfigGetCurrent();
1614 
1615   if (!XRE_IsParentProcess()) {
1616 #ifdef MOZ_WIDGET_GTK
1617     auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1618     UpdateSystemFontOptionsFromIpc(fontList.options());
1619 #endif
1620     // Content processes will access the shared-memory data created by the
1621     // parent, so they do not need to query fontconfig for the available
1622     // fonts themselves.
1623     return;
1624   }
1625 
1626 #ifdef MOZ_WIDGET_GTK
1627   UpdateSystemFontOptions();
1628 #endif
1629 
1630 #ifdef MOZ_BUNDLED_FONTS
1631   if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1632     TimeStamp start = TimeStamp::Now();
1633     ActivateBundledFonts();
1634     TimeStamp end = TimeStamp::Now();
1635     Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1636                           (end - start).ToMilliseconds());
1637   }
1638 #endif
1639 
1640   UniquePtr<SandboxPolicy> policy;
1641 
1642 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1643   // If read sandboxing is enabled, create a temporary SandboxPolicy to
1644   // check font paths; use a fake PID to avoid picking up any PID-specific
1645   // rules by accident.
1646   SandboxBrokerPolicyFactory policyFactory;
1647   if (GetEffectiveContentSandboxLevel() > 2 &&
1648       !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1649     policy = policyFactory.GetContentPolicy(-1, false);
1650   }
1651 #endif
1652 
1653   nsTArray<fontlist::Family::InitData> families;
1654 
1655   nsClassHashtable<nsCStringHashKey, FacesData> faces;
1656 
1657   // Do we need to work around the fontconfig FcNameParse/FcNameUnparse bug
1658   // (present in versions between 2.10.94 and 2.11.1 inclusive)? See comment
1659   // in InitFontListForPlatform for details.
1660   int fcVersion = FcGetVersion();
1661   bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1662 
1663   auto addPattern = [this, fcCharsetParseBug, &families, &faces](
1664                         FcPattern* aPattern, FcChar8*& aLastFamilyName,
1665                         nsCString& aFamilyName, bool aAppFont) -> void {
1666     // get canonical name
1667     uint32_t cIndex = FindCanonicalNameIndex(aPattern, FC_FAMILYLANG);
1668     FcChar8* canonical = nullptr;
1669     FcPatternGetString(aPattern, FC_FAMILY, cIndex, &canonical);
1670     if (!canonical) {
1671       return;
1672     }
1673 
1674     nsAutoCString keyName;
1675     keyName = ToCharPtr(canonical);
1676     ToLowerCase(keyName);
1677 
1678     aLastFamilyName = canonical;
1679     aFamilyName = ToCharPtr(canonical);
1680 
1681     // Same canonical family name as the last one? Definitely no need to add a
1682     // new family record.
1683     auto* faceList =
1684         faces
1685             .LookupOrInsertWith(
1686                 keyName,
1687                 [&] {
1688                   FontVisibility visibility =
1689                       aAppFont ? FontVisibility::Base
1690                                : GetVisibilityForFamily(keyName);
1691                   families.AppendElement(fontlist::Family::InitData(
1692                       keyName, aFamilyName, fontlist::Family::kNoIndex,
1693                       visibility,
1694                       /*bundled*/ aAppFont, /*badUnderline*/ false));
1695                   return MakeUnique<FacesData>();
1696                 })
1697             .get();
1698 
1699     char* s = (char*)FcNameUnparse(aPattern);
1700     nsAutoCString descriptor(s);
1701     free(s);
1702 
1703     if (fcCharsetParseBug) {
1704       // Escape any leading space in charset to work around FcNameParse bug.
1705       int32_t index = descriptor.Find(":charset= ");
1706       if (index != kNotFound) {
1707         // insert backslash after the =, before the space
1708         descriptor.Insert('\\', index + 9);
1709       }
1710     }
1711 
1712     WeightRange weight(FontWeight::Normal());
1713     StretchRange stretch(FontStretch::Normal());
1714     SlantStyleRange style(FontSlantStyle::Normal());
1715     uint16_t size;
1716     GetFontProperties(aPattern, &weight, &stretch, &style, &size);
1717 
1718     auto initData = fontlist::Face::InitData{descriptor, 0,       size, false,
1719                                              weight,     stretch, style};
1720 
1721     // Add entries for any other localized family names. (Most fonts only have
1722     // a single family name, so the first call to GetString will usually fail).
1723     FcChar8* otherName;
1724     int n = (cIndex == 0 ? 1 : 0);
1725     while (FcPatternGetString(aPattern, FC_FAMILY, n, &otherName) ==
1726            FcResultMatch) {
1727       nsAutoCString otherFamilyName(ToCharPtr(otherName));
1728       keyName = otherFamilyName;
1729       ToLowerCase(keyName);
1730 
1731       faces
1732           .LookupOrInsertWith(
1733               keyName,
1734               [&] {
1735                 FontVisibility visibility =
1736                     aAppFont ? FontVisibility::Base
1737                              : GetVisibilityForFamily(keyName);
1738                 families.AppendElement(fontlist::Family::InitData(
1739                     keyName, otherFamilyName, fontlist::Family::kNoIndex,
1740                     visibility,
1741                     /*bundled*/ aAppFont, /*badUnderline*/ false));
1742 
1743                 return MakeUnique<FacesData>();
1744               })
1745           .get()
1746           ->Add(fontlist::Face::InitData(initData), /* singleName = */ false);
1747 
1748       n++;
1749       if (n == int(cIndex)) {
1750         n++;  // skip over canonical name
1751       }
1752     }
1753 
1754     const bool singleName = n == 1;
1755     faceList->Add(std::move(initData), singleName);
1756 
1757     // map the psname, fullname ==> font family for local font lookups
1758     nsAutoCString psname, fullname;
1759     GetFaceNames(aPattern, aFamilyName, psname, fullname);
1760     if (!psname.IsEmpty()) {
1761       ToLowerCase(psname);
1762       mLocalNameTable.InsertOrUpdate(
1763           psname, fontlist::LocalFaceRec::InitData(keyName, descriptor));
1764     }
1765     if (!fullname.IsEmpty()) {
1766       ToLowerCase(fullname);
1767       if (fullname != psname) {
1768         mLocalNameTable.WithEntryHandle(fullname, [&](auto&& entry) {
1769           if (entry && !singleName) {
1770             // We only override an existing entry if this is the only way to
1771             // name this family. This prevents dubious aliases from clobbering
1772             // the local name table.
1773             return;
1774           }
1775           entry.InsertOrUpdate(
1776               fontlist::LocalFaceRec::InitData(keyName, descriptor));
1777         });
1778       }
1779     }
1780   };
1781 
1782   auto addFontSetFamilies = [&addPattern](FcFontSet* aFontSet,
1783                                           SandboxPolicy* aPolicy,
1784                                           bool aAppFonts) -> void {
1785     FcChar8* lastFamilyName = (FcChar8*)"";
1786     RefPtr<gfxFontconfigFontFamily> fontFamily;
1787     nsAutoCString familyName;
1788     for (int f = 0; f < aFontSet->nfont; f++) {
1789       FcPattern* pattern = aFontSet->fonts[f];
1790 
1791       // Skip any fonts that aren't readable for us (e.g. due to restrictive
1792       // file ownership/permissions).
1793       FcChar8* path;
1794       if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1795         continue;
1796       }
1797       if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1798         continue;
1799       }
1800 
1801 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1802       // Skip any fonts that will be blocked by the content-process sandbox
1803       // policy.
1804       if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1805                        SandboxBroker::Perms::MAY_READ)) {
1806         continue;
1807       }
1808 #endif
1809 
1810       // If this is a TrueType or OpenType font, discard the FC_CHARSET object
1811       // (which may be very large), because we'll read the 'cmap' directly.
1812       // This substantially reduces the pressure on shared memory (bug 1664151)
1813       // due to the large font descriptors (serialized patterns).
1814       FcChar8* fontFormat;
1815       if (FcPatternGetString(pattern, FC_FONTFORMAT, 0, &fontFormat) ==
1816               FcResultMatch &&
1817           (!FcStrCmp(fontFormat, (const FcChar8*)"TrueType") ||
1818            !FcStrCmp(fontFormat, (const FcChar8*)"CFF"))) {
1819         FcPattern* clone = FcPatternDuplicate(pattern);
1820         FcPatternDel(clone, FC_CHARSET);
1821         addPattern(clone, lastFamilyName, familyName, aAppFonts);
1822         FcPatternDestroy(clone);
1823       } else {
1824         addPattern(pattern, lastFamilyName, familyName, aAppFonts);
1825       }
1826     }
1827   };
1828 
1829   // iterate over available fonts
1830   FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1831   addFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
1832 
1833 #ifdef MOZ_BUNDLED_FONTS
1834   if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1835     FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1836     addFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1837   }
1838 #endif
1839 
1840   mozilla::fontlist::FontList* list = SharedFontList();
1841   list->SetFamilyNames(families);
1842 
1843   for (uint32_t i = 0; i < families.Length(); i++) {
1844     list->Families()[i].AddFaces(list, faces.Get(families[i].mKey)->Get());
1845   }
1846 }
1847 
GetDistroID() const1848 gfxFcPlatformFontList::DistroID gfxFcPlatformFontList::GetDistroID() const {
1849   // Helper called to initialize sResult the first time this is used.
1850   auto getDistroID = []() {
1851     DistroID result = DistroID::Unknown;
1852     FILE* fp = fopen("/etc/os-release", "r");
1853     if (fp) {
1854       char buf[512];
1855       while (fgets(buf, sizeof(buf), fp)) {
1856         if (strncmp(buf, "ID=", 3) == 0) {
1857           if (strncmp(buf + 3, "ubuntu", 6) == 0) {
1858             result = DistroID::Ubuntu;
1859           } else if (strncmp(buf + 3, "fedora", 6) == 0) {
1860             result = DistroID::Fedora;
1861           }
1862           break;
1863         }
1864       }
1865       fclose(fp);
1866     }
1867     return result;
1868   };
1869   static DistroID sResult = getDistroID();
1870   return sResult;
1871 }
1872 
GetVisibilityForFamily(const nsACString & aName) const1873 FontVisibility gfxFcPlatformFontList::GetVisibilityForFamily(
1874     const nsACString& aName) const {
1875   switch (GetDistroID()) {
1876     case DistroID::Ubuntu:
1877       if (FamilyInList(aName, kBaseFonts_Ubuntu_20_04,
1878                        ArrayLength(kBaseFonts_Ubuntu_20_04))) {
1879         return FontVisibility::Base;
1880       }
1881       if (FamilyInList(aName, kLangFonts_Ubuntu_20_04,
1882                        ArrayLength(kLangFonts_Ubuntu_20_04))) {
1883         return FontVisibility::LangPack;
1884       }
1885       return FontVisibility::User;
1886     case DistroID::Fedora:
1887       if (FamilyInList(aName, kBaseFonts_Fedora_32,
1888                        ArrayLength(kBaseFonts_Fedora_32))) {
1889         return FontVisibility::Base;
1890       }
1891       return FontVisibility::User;
1892     default:
1893       // We don't know how to categorize fonts on this system
1894       return FontVisibility::Unknown;
1895   }
1896 }
1897 
CreateFontEntry(fontlist::Face * aFace,const fontlist::Family * aFamily)1898 gfxFontEntry* gfxFcPlatformFontList::CreateFontEntry(
1899     fontlist::Face* aFace, const fontlist::Family* aFamily) {
1900   nsAutoCString desc(aFace->mDescriptor.AsString(SharedFontList()));
1901   FcPattern* pattern = FcNameParse((const FcChar8*)desc.get());
1902   auto* fe = new gfxFontconfigFontEntry(desc, pattern, true);
1903   FcPatternDestroy(pattern);
1904   fe->InitializeFrom(aFace, aFamily);
1905   return fe;
1906 }
1907 
1908 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
1909 // FcFontList results in the list containing the localized names as dictated
1910 // by system defaults.
GetSystemFontList(nsTArray<nsString> & aListOfFonts,nsAtom * aLangGroup)1911 static void GetSystemFontList(nsTArray<nsString>& aListOfFonts,
1912                               nsAtom* aLangGroup) {
1913   aListOfFonts.Clear();
1914 
1915   RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
1916   if (!pat) {
1917     return;
1918   }
1919 
1920   UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
1921   if (!os) {
1922     return;
1923   }
1924 
1925   // add the lang to the pattern
1926   nsAutoCString fcLang;
1927   gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1928   pfl->GetSampleLangForGroup(aLangGroup, fcLang,
1929                              /*aForFontEnumerationThread*/ true);
1930   if (!fcLang.IsEmpty()) {
1931     FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
1932   }
1933 
1934   UniquePtr<FcFontSet> fs(FcFontList(nullptr, pat, os.get()));
1935   if (!fs) {
1936     return;
1937   }
1938 
1939   for (int i = 0; i < fs->nfont; i++) {
1940     char* family;
1941 
1942     if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8**)&family) !=
1943         FcResultMatch) {
1944       continue;
1945     }
1946 
1947     // Remove duplicates...
1948     nsAutoString strFamily;
1949     AppendUTF8toUTF16(MakeStringSpan(family), strFamily);
1950     if (aListOfFonts.Contains(strFamily)) {
1951       continue;
1952     }
1953 
1954     aListOfFonts.AppendElement(strFamily);
1955   }
1956 
1957   aListOfFonts.Sort();
1958 }
1959 
GetFontList(nsAtom * aLangGroup,const nsACString & aGenericFamily,nsTArray<nsString> & aListOfFonts)1960 void gfxFcPlatformFontList::GetFontList(nsAtom* aLangGroup,
1961                                         const nsACString& aGenericFamily,
1962                                         nsTArray<nsString>& aListOfFonts) {
1963   // Get the list of font family names using fontconfig
1964   GetSystemFontList(aListOfFonts, aLangGroup);
1965 
1966   // Under Linux, the generics "serif", "sans-serif" and "monospace"
1967   // are included in the pref fontlist. These map to whatever fontconfig
1968   // decides they should be for a given language, rather than one of the
1969   // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
1970   bool serif = false, sansSerif = false, monospace = false;
1971   if (aGenericFamily.IsEmpty())
1972     serif = sansSerif = monospace = true;
1973   else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
1974     serif = true;
1975   else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
1976     sansSerif = true;
1977   else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
1978     monospace = true;
1979   else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
1980            aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
1981     serif = sansSerif = true;
1982   else
1983     MOZ_ASSERT_UNREACHABLE("unexpected CSS generic font family");
1984 
1985   // The first in the list becomes the default in
1986   // FontBuilder.readFontSelection() if the preference-selected font is not
1987   // available, so put system configured defaults first.
1988   if (monospace) aListOfFonts.InsertElementAt(0, u"monospace"_ns);
1989   if (sansSerif) aListOfFonts.InsertElementAt(0, u"sans-serif"_ns);
1990   if (serif) aListOfFonts.InsertElementAt(0, u"serif"_ns);
1991 }
1992 
GetDefaultFontForPlatform(const gfxFontStyle * aStyle,nsAtom * aLanguage)1993 FontFamily gfxFcPlatformFontList::GetDefaultFontForPlatform(
1994     const gfxFontStyle* aStyle, nsAtom* aLanguage) {
1995   // Get the default font by using a fake name to retrieve the first
1996   // scalable font that fontconfig suggests for the given language.
1997   PrefFontList* prefFonts = FindGenericFamilies(
1998       "-moz-default"_ns, aLanguage ? aLanguage : nsGkAtoms::x_western);
1999   NS_ASSERTION(prefFonts, "null list of generic fonts");
2000   if (prefFonts && !prefFonts->IsEmpty()) {
2001     return (*prefFonts)[0];
2002   }
2003   return FontFamily();
2004 }
2005 
LookupLocalFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry)2006 gfxFontEntry* gfxFcPlatformFontList::LookupLocalFont(
2007     const nsACString& aFontName, WeightRange aWeightForEntry,
2008     StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
2009   nsAutoCString keyName(aFontName);
2010   ToLowerCase(keyName);
2011 
2012   if (SharedFontList()) {
2013     return LookupInSharedFaceNameList(aFontName, aWeightForEntry,
2014                                       aStretchForEntry, aStyleForEntry);
2015   }
2016 
2017   // if name is not in the global list, done
2018   const auto fontPattern = mLocalNames.Lookup(keyName);
2019   if (!fontPattern) {
2020     return nullptr;
2021   }
2022 
2023   return new gfxFontconfigFontEntry(aFontName, *fontPattern, aWeightForEntry,
2024                                     aStretchForEntry, aStyleForEntry);
2025 }
2026 
MakePlatformFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry,const uint8_t * aFontData,uint32_t aLength)2027 gfxFontEntry* gfxFcPlatformFontList::MakePlatformFont(
2028     const nsACString& aFontName, WeightRange aWeightForEntry,
2029     StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
2030     const uint8_t* aFontData, uint32_t aLength) {
2031   RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength);
2032   RefPtr<SharedFTFace> face = ufd->CloneFace();
2033   if (!face) {
2034     return nullptr;
2035   }
2036   return new gfxFontconfigFontEntry(aFontName, aWeightForEntry,
2037                                     aStretchForEntry, aStyleForEntry,
2038                                     std::move(face));
2039 }
2040 
FindAndAddFamilies(StyleGenericFontFamily aGeneric,const nsACString & aFamily,nsTArray<FamilyAndGeneric> * aOutput,FindFamiliesFlags aFlags,gfxFontStyle * aStyle,nsAtom * aLanguage,gfxFloat aDevToCssSize)2041 bool gfxFcPlatformFontList::FindAndAddFamilies(
2042     StyleGenericFontFamily aGeneric, const nsACString& aFamily,
2043     nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
2044     gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
2045   nsAutoCString familyName(aFamily);
2046   ToLowerCase(familyName);
2047 
2048   if (!(aFlags & FindFamiliesFlags::eQuotedFamilyName)) {
2049     // deprecated generic names are explicitly converted to standard generics
2050     bool isDeprecatedGeneric = false;
2051     if (familyName.EqualsLiteral("sans") ||
2052         familyName.EqualsLiteral("sans serif")) {
2053       familyName.AssignLiteral("sans-serif");
2054       isDeprecatedGeneric = true;
2055     } else if (familyName.EqualsLiteral("mono")) {
2056       familyName.AssignLiteral("monospace");
2057       isDeprecatedGeneric = true;
2058     }
2059 
2060     // fontconfig generics? use fontconfig to determine the family for lang
2061     if (isDeprecatedGeneric ||
2062         mozilla::StyleSingleFontFamily::Parse(familyName).IsGeneric()) {
2063       PrefFontList* prefFonts = FindGenericFamilies(familyName, aLanguage);
2064       if (prefFonts && !prefFonts->IsEmpty()) {
2065         aOutput->AppendElements(*prefFonts);
2066         return true;
2067       }
2068       return false;
2069     }
2070   }
2071 
2072   // fontconfig allows conditional substitutions in such a way that it's
2073   // difficult to distinguish an explicit substitution from other suggested
2074   // choices. To sniff out explicit substitutions, compare the substitutions
2075   // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
2076   // substitutions
2077   //
2078   // Example:
2079   //
2080   //   serif ==> DejaVu Serif, ...
2081   //   Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu
2082   //   Serif
2083   //
2084   // In this case fontconfig is including Tex Gyre Heros and
2085   // Nimbus Sans L as alternatives for Helvetica.
2086 
2087   // Because the FcConfigSubstitute call is quite expensive, we cache the
2088   // actual font families found via this process. So check the cache first:
2089   if (auto cachedFamilies = mFcSubstituteCache.Lookup(familyName)) {
2090     if (cachedFamilies->IsEmpty()) {
2091       return false;
2092     }
2093     aOutput->AppendElements(*cachedFamilies);
2094     return true;
2095   }
2096 
2097   // It wasn't in the cache, so we need to ask fontconfig...
2098   const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
2099   FcChar8* sentinelFirstFamily = nullptr;
2100   RefPtr<FcPattern> sentinelSubst = dont_AddRef(FcPatternCreate());
2101   FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
2102   FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
2103   FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
2104 
2105   // substitutions for font, -moz-sentinel pattern
2106   RefPtr<FcPattern> fontWithSentinel = dont_AddRef(FcPatternCreate());
2107   FcPatternAddString(fontWithSentinel, FC_FAMILY,
2108                      ToFcChar8Ptr(familyName.get()));
2109   FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
2110   FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
2111 
2112   // Add all font family matches until reaching the sentinel.
2113   AutoTArray<FamilyAndGeneric, 10> cachedFamilies;
2114   FcChar8* substName = nullptr;
2115   for (int i = 0; FcPatternGetString(fontWithSentinel, FC_FAMILY, i,
2116                                      &substName) == FcResultMatch;
2117        i++) {
2118     if (sentinelFirstFamily && FcStrCmp(substName, sentinelFirstFamily) == 0) {
2119       break;
2120     }
2121     gfxPlatformFontList::FindAndAddFamilies(
2122         aGeneric, nsDependentCString(ToCharPtr(substName)), &cachedFamilies,
2123         aFlags, aStyle, aLanguage);
2124   }
2125 
2126   // Cache the resulting list, so we don't have to do this again.
2127   const auto& insertedCachedFamilies =
2128       mFcSubstituteCache.InsertOrUpdate(familyName, std::move(cachedFamilies));
2129 
2130   if (insertedCachedFamilies.IsEmpty()) {
2131     return false;
2132   }
2133   aOutput->AppendElements(insertedCachedFamilies);
2134   return true;
2135 }
2136 
GetStandardFamilyName(const nsCString & aFontName,nsACString & aFamilyName)2137 bool gfxFcPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
2138                                                   nsACString& aFamilyName) {
2139   aFamilyName.Truncate();
2140 
2141   // The fontconfig list of fonts includes generic family names in the
2142   // font list. For these, just use the generic name.
2143   if (aFontName.EqualsLiteral("serif") ||
2144       aFontName.EqualsLiteral("sans-serif") ||
2145       aFontName.EqualsLiteral("monospace")) {
2146     aFamilyName.Assign(aFontName);
2147     return true;
2148   }
2149 
2150   RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
2151   if (!pat) {
2152     return true;
2153   }
2154 
2155   UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
2156   if (!os) {
2157     return true;
2158   }
2159 
2160   // add the family name to the pattern
2161   FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(aFontName.get()));
2162 
2163   UniquePtr<FcFontSet> givenFS(FcFontList(nullptr, pat, os.get()));
2164   if (!givenFS) {
2165     return true;
2166   }
2167 
2168   // See if there is a font face with first family equal to the given family
2169   // (needs to be in sync with names coming from GetFontList())
2170   nsTArray<nsCString> candidates;
2171   for (int i = 0; i < givenFS->nfont; i++) {
2172     char* firstFamily;
2173 
2174     if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
2175                            (FcChar8**)&firstFamily) != FcResultMatch) {
2176       continue;
2177     }
2178 
2179     nsDependentCString first(firstFamily);
2180     if (!candidates.Contains(first)) {
2181       candidates.AppendElement(first);
2182 
2183       if (aFontName.Equals(first)) {
2184         aFamilyName.Assign(aFontName);
2185         return true;
2186       }
2187     }
2188   }
2189 
2190   // Because fontconfig conflates different family name types, need to
2191   // double check that the candidate name is not simply a different
2192   // name type. For example, if a font with nameID=16 "Minion Pro" and
2193   // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
2194   // family="Minion Pro" will return a set of patterns some of which
2195   // will have a first family of "Minion Pro Caption". Ignore these
2196   // patterns and use the first candidate that maps to a font set with
2197   // the same number of faces and an identical set of patterns.
2198   for (uint32_t j = 0; j < candidates.Length(); ++j) {
2199     FcPatternDel(pat, FC_FAMILY);
2200     FcPatternAddString(pat, FC_FAMILY, (FcChar8*)candidates[j].get());
2201 
2202     UniquePtr<FcFontSet> candidateFS(FcFontList(nullptr, pat, os.get()));
2203     if (!candidateFS) {
2204       return true;
2205     }
2206 
2207     if (candidateFS->nfont != givenFS->nfont) {
2208       continue;
2209     }
2210 
2211     bool equal = true;
2212     for (int i = 0; i < givenFS->nfont; ++i) {
2213       if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
2214         equal = false;
2215         break;
2216       }
2217     }
2218     if (equal) {
2219       aFamilyName = candidates[j];
2220       return true;
2221     }
2222   }
2223 
2224   // didn't find localized name, leave family name blank
2225   return true;
2226 }
2227 
AddGenericFonts(StyleGenericFontFamily aGenericType,nsAtom * aLanguage,nsTArray<FamilyAndGeneric> & aFamilyList)2228 void gfxFcPlatformFontList::AddGenericFonts(
2229     StyleGenericFontFamily aGenericType, nsAtom* aLanguage,
2230     nsTArray<FamilyAndGeneric>& aFamilyList) {
2231   bool usePrefFontList = false;
2232 
2233   const char* generic = GetGenericName(aGenericType);
2234   NS_ASSERTION(generic, "weird generic font type");
2235   if (!generic) {
2236     return;
2237   }
2238 
2239   // By default, most font prefs on Linux map to "use fontconfig"
2240   // keywords. So only need to explicitly lookup font pref if
2241   // non-default settings exist
2242   nsAutoCString genericToLookup(generic);
2243   if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
2244       aLanguage == nsGkAtoms::x_math) {
2245     nsAtom* langGroup = GetLangGroup(aLanguage);
2246     nsAutoString fontlistValue;
2247     Preferences::GetString(NamePref(generic, langGroup).get(), fontlistValue);
2248     nsresult rv;
2249     if (fontlistValue.IsEmpty()) {
2250       // The font name list may have two or more family names as comma
2251       // separated list.  In such case, not matching with generic font
2252       // name is fine because if the list prefers specific font, we
2253       // should try to use the pref with complicated path.
2254       rv = Preferences::GetString(NameListPref(generic, langGroup).get(),
2255                                   fontlistValue);
2256     } else {
2257       rv = NS_OK;
2258     }
2259     if (NS_SUCCEEDED(rv)) {
2260       if (!fontlistValue.EqualsLiteral("serif") &&
2261           !fontlistValue.EqualsLiteral("sans-serif") &&
2262           !fontlistValue.EqualsLiteral("monospace")) {
2263         usePrefFontList = true;
2264       } else {
2265         // serif, sans-serif or monospace was specified
2266         genericToLookup.Truncate();
2267         AppendUTF16toUTF8(fontlistValue, genericToLookup);
2268       }
2269     }
2270   }
2271 
2272   // when pref fonts exist, use standard pref font lookup
2273   if (usePrefFontList) {
2274     return gfxPlatformFontList::AddGenericFonts(aGenericType, aLanguage,
2275                                                 aFamilyList);
2276   }
2277 
2278   PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
2279   NS_ASSERTION(prefFonts, "null generic font list");
2280   aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
2281   for (auto& f : *prefFonts) {
2282     aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
2283   }
2284 }
2285 
ClearLangGroupPrefFonts()2286 void gfxFcPlatformFontList::ClearLangGroupPrefFonts() {
2287   ClearGenericMappings();
2288   gfxPlatformFontList::ClearLangGroupPrefFonts();
2289   mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
2290 }
2291 
FindGenericFamilies(const nsCString & aGeneric,nsAtom * aLanguage)2292 gfxPlatformFontList::PrefFontList* gfxFcPlatformFontList::FindGenericFamilies(
2293     const nsCString& aGeneric, nsAtom* aLanguage) {
2294   // set up name
2295   nsAutoCString fcLang;
2296   GetSampleLangForGroup(aLanguage, fcLang);
2297   ToLowerCase(fcLang);
2298 
2299   nsAutoCString genericLang(aGeneric);
2300   if (fcLang.Length() > 0) {
2301     genericLang.Append('-');
2302   }
2303   genericLang.Append(fcLang);
2304 
2305   // try to get the family from the cache
2306   return mGenericMappings.WithEntryHandle(
2307       genericLang, [&](auto&& entry) -> PrefFontList* {
2308         if (!entry) {
2309           // if not found, ask fontconfig to pick the appropriate font
2310           RefPtr<FcPattern> genericPattern = dont_AddRef(FcPatternCreate());
2311           FcPatternAddString(genericPattern, FC_FAMILY,
2312                              ToFcChar8Ptr(aGeneric.get()));
2313 
2314           // -- prefer scalable fonts
2315           FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
2316 
2317           // -- add the lang to the pattern
2318           if (!fcLang.IsEmpty()) {
2319             FcPatternAddString(genericPattern, FC_LANG,
2320                                ToFcChar8Ptr(fcLang.get()));
2321           }
2322 
2323           // -- perform substitutions
2324           FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
2325           FcDefaultSubstitute(genericPattern);
2326 
2327           // -- sort to get the closest matches
2328           FcResult result;
2329           UniquePtr<FcFontSet> faces(
2330               FcFontSort(nullptr, genericPattern, FcFalse, nullptr, &result));
2331 
2332           if (!faces) {
2333             return nullptr;
2334           }
2335 
2336           // -- select the fonts to be used for the generic
2337           auto prefFonts = MakeUnique<PrefFontList>();  // can be empty but in
2338                                                         // practice won't happen
2339           uint32_t limit =
2340               gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
2341           bool foundFontWithLang = false;
2342           for (int i = 0; i < faces->nfont; i++) {
2343             FcPattern* font = faces->fonts[i];
2344             FcChar8* mappedGeneric = nullptr;
2345 
2346             FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
2347             if (mappedGeneric) {
2348               nsAutoCString mappedGenericName(ToCharPtr(mappedGeneric));
2349               AutoTArray<FamilyAndGeneric, 1> genericFamilies;
2350               if (gfxPlatformFontList::FindAndAddFamilies(
2351                       StyleGenericFontFamily::None, mappedGenericName,
2352                       &genericFamilies, FindFamiliesFlags(0))) {
2353                 MOZ_ASSERT(genericFamilies.Length() == 1,
2354                            "expected a single family");
2355                 if (!prefFonts->Contains(genericFamilies[0].mFamily)) {
2356                   prefFonts->AppendElement(genericFamilies[0].mFamily);
2357                   bool foundLang =
2358                       !fcLang.IsEmpty() &&
2359                       PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
2360                   foundFontWithLang = foundFontWithLang || foundLang;
2361                   // check to see if the list is full
2362                   if (prefFonts->Length() >= limit) {
2363                     break;
2364                   }
2365                 }
2366               }
2367             }
2368           }
2369 
2370           // if no font in the list matches the lang, trim all but the first one
2371           if (!prefFonts->IsEmpty() && !foundFontWithLang) {
2372             prefFonts->TruncateLength(1);
2373           }
2374 
2375           entry.Insert(std::move(prefFonts));
2376         }
2377         return entry->get();
2378       });
2379 }
2380 
PrefFontListsUseOnlyGenerics()2381 bool gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics() {
2382   static const char kFontNamePrefix[] = "font.name.";
2383 
2384   bool prefFontsUseOnlyGenerics = true;
2385   nsTArray<nsCString> names;
2386   nsresult rv =
2387       Preferences::GetRootBranch()->GetChildList(kFontNamePrefix, names);
2388   if (NS_SUCCEEDED(rv)) {
2389     for (auto& name : names) {
2390       // Check whether all font.name prefs map to generic keywords
2391       // and that the pref name and keyword match.
2392       //   Ex: font.name.serif.ar ==> "serif" (ok)
2393       //   Ex: font.name.serif.ar ==> "monospace" (return false)
2394       //   Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
2395       //   Ex: font.name.serif.ar ==> "" and
2396       //       font.name-list.serif.ar ==> "serif" (ok)
2397       //   Ex: font.name.serif.ar ==> "" and
2398       //       font.name-list.serif.ar ==> "Something, serif"
2399       //                                           (return false)
2400 
2401       nsDependentCSubstring prefName =
2402           Substring(name, ArrayLength(kFontNamePrefix) - 1);
2403       nsCCharSeparatedTokenizer tokenizer(prefName, '.');
2404       const nsDependentCSubstring& generic = tokenizer.nextToken();
2405       const nsDependentCSubstring& langGroup = tokenizer.nextToken();
2406       nsAutoCString fontPrefValue;
2407       Preferences::GetCString(name.get(), fontPrefValue);
2408       if (fontPrefValue.IsEmpty()) {
2409         // The font name list may have two or more family names as comma
2410         // separated list.  In such case, not matching with generic font
2411         // name is fine because if the list prefers specific font, this
2412         // should return false.
2413         Preferences::GetCString(NameListPref(generic, langGroup).get(),
2414                                 fontPrefValue);
2415       }
2416 
2417       if (!langGroup.EqualsLiteral("x-math") &&
2418           !generic.Equals(fontPrefValue)) {
2419         prefFontsUseOnlyGenerics = false;
2420         break;
2421       }
2422     }
2423   }
2424   return prefFontsUseOnlyGenerics;
2425 }
2426 
2427 /* static */
CheckFontUpdates(nsITimer * aTimer,void * aThis)2428 void gfxFcPlatformFontList::CheckFontUpdates(nsITimer* aTimer, void* aThis) {
2429   // A content process is not supposed to check this directly;
2430   // it will be notified by the parent when the font list changes.
2431   MOZ_ASSERT(XRE_IsParentProcess());
2432 
2433   // check for font updates
2434   FcInitBringUptoDate();
2435 
2436   // update fontlist if current config changed
2437   gfxFcPlatformFontList* pfl = static_cast<gfxFcPlatformFontList*>(aThis);
2438   FcConfig* current = FcConfigGetCurrent();
2439   if (current != pfl->GetLastConfig()) {
2440     pfl->UpdateFontList();
2441     pfl->ForceGlobalReflow();
2442 
2443     mozilla::dom::ContentParent::NotifyUpdatedFonts(true);
2444   }
2445 }
2446 
CreateFontFamily(const nsACString & aName,FontVisibility aVisibility) const2447 gfxFontFamily* gfxFcPlatformFontList::CreateFontFamily(
2448     const nsACString& aName, FontVisibility aVisibility) const {
2449   return new gfxFontconfigFontFamily(aName, aVisibility);
2450 }
2451 
2452 // mapping of moz lang groups ==> default lang
2453 struct MozLangGroupData {
2454   nsAtom* const& mozLangGroup;
2455   const char* defaultLang;
2456 };
2457 
2458 const MozLangGroupData MozLangGroups[] = {
2459     {nsGkAtoms::x_western, "en"},    {nsGkAtoms::x_cyrillic, "ru"},
2460     {nsGkAtoms::x_devanagari, "hi"}, {nsGkAtoms::x_tamil, "ta"},
2461     {nsGkAtoms::x_armn, "hy"},       {nsGkAtoms::x_beng, "bn"},
2462     {nsGkAtoms::x_cans, "iu"},       {nsGkAtoms::x_ethi, "am"},
2463     {nsGkAtoms::x_geor, "ka"},       {nsGkAtoms::x_gujr, "gu"},
2464     {nsGkAtoms::x_guru, "pa"},       {nsGkAtoms::x_khmr, "km"},
2465     {nsGkAtoms::x_knda, "kn"},       {nsGkAtoms::x_mlym, "ml"},
2466     {nsGkAtoms::x_orya, "or"},       {nsGkAtoms::x_sinh, "si"},
2467     {nsGkAtoms::x_tamil, "ta"},      {nsGkAtoms::x_telu, "te"},
2468     {nsGkAtoms::x_tibt, "bo"},       {nsGkAtoms::Unicode, 0}};
2469 
TryLangForGroup(const nsACString & aOSLang,nsAtom * aLangGroup,nsACString & aFcLang,bool aForFontEnumerationThread)2470 bool gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
2471                                             nsAtom* aLangGroup,
2472                                             nsACString& aFcLang,
2473                                             bool aForFontEnumerationThread) {
2474   // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
2475   // aOSLang is in the form "language[_territory][.codeset][@modifier]".
2476   // fontconfig takes languages in the form "language-territory".
2477   // nsLanguageAtomService takes languages in the form language-subtag,
2478   // where subtag may be a territory.  fontconfig and nsLanguageAtomService
2479   // handle case-conversion for us.
2480   const char *pos, *end;
2481   aOSLang.BeginReading(pos);
2482   aOSLang.EndReading(end);
2483   aFcLang.Truncate();
2484   while (pos < end) {
2485     switch (*pos) {
2486       case '.':
2487       case '@':
2488         end = pos;
2489         break;
2490       case '_':
2491         aFcLang.Append('-');
2492         break;
2493       default:
2494         aFcLang.Append(*pos);
2495     }
2496     ++pos;
2497   }
2498 
2499   if (!aForFontEnumerationThread) {
2500     nsAtom* atom = mLangService->LookupLanguage(aFcLang);
2501     return atom == aLangGroup;
2502   }
2503 
2504   // If we were called by the font enumeration thread, we can't use
2505   // mLangService->LookupLanguage because it is not thread-safe.
2506   // Use GetUncachedLanguageGroup to avoid unsafe access to the lang-group
2507   // mapping cache hashtable.
2508   nsAutoCString lowered(aFcLang);
2509   ToLowerCase(lowered);
2510   RefPtr<nsAtom> lang = NS_Atomize(lowered);
2511   RefPtr<nsAtom> group = mLangService->GetUncachedLanguageGroup(lang);
2512   return group.get() == aLangGroup;
2513 }
2514 
GetSampleLangForGroup(nsAtom * aLanguage,nsACString & aLangStr,bool aForFontEnumerationThread)2515 void gfxFcPlatformFontList::GetSampleLangForGroup(
2516     nsAtom* aLanguage, nsACString& aLangStr, bool aForFontEnumerationThread) {
2517   aLangStr.Truncate();
2518   if (!aLanguage) {
2519     return;
2520   }
2521 
2522   // set up lang string
2523   const MozLangGroupData* mozLangGroup = nullptr;
2524 
2525   // -- look it up in the list of moz lang groups
2526   for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
2527     if (aLanguage == MozLangGroups[i].mozLangGroup) {
2528       mozLangGroup = &MozLangGroups[i];
2529       break;
2530     }
2531   }
2532 
2533   // -- not a mozilla lang group? Just return the BCP47 string
2534   //    representation of the lang group
2535   if (!mozLangGroup) {
2536     // Not a special mozilla language group.
2537     // Use aLanguage as a language code.
2538     aLanguage->ToUTF8String(aLangStr);
2539     return;
2540   }
2541 
2542   // -- check the environment for the user's preferred language that
2543   //    corresponds to this mozilla lang group.
2544   const char* languages = getenv("LANGUAGE");
2545   if (languages) {
2546     const char separator = ':';
2547 
2548     for (const char* pos = languages; true; ++pos) {
2549       if (*pos == '\0' || *pos == separator) {
2550         if (languages < pos &&
2551             TryLangForGroup(Substring(languages, pos), aLanguage, aLangStr,
2552                             aForFontEnumerationThread)) {
2553           return;
2554         }
2555 
2556         if (*pos == '\0') {
2557           break;
2558         }
2559 
2560         languages = pos + 1;
2561       }
2562     }
2563   }
2564   const char* ctype = setlocale(LC_CTYPE, nullptr);
2565   if (ctype && TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr,
2566                                aForFontEnumerationThread)) {
2567     return;
2568   }
2569 
2570   if (mozLangGroup->defaultLang) {
2571     aLangStr.Assign(mozLangGroup->defaultLang);
2572   } else {
2573     aLangStr.Truncate();
2574   }
2575 }
2576 
2577 #ifdef MOZ_BUNDLED_FONTS
ActivateBundledFonts()2578 void gfxFcPlatformFontList::ActivateBundledFonts() {
2579   if (!mBundledFontsInitialized) {
2580     mBundledFontsInitialized = true;
2581     nsCOMPtr<nsIFile> localDir;
2582     nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2583     if (NS_FAILED(rv)) {
2584       return;
2585     }
2586     if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
2587       return;
2588     }
2589     bool isDir;
2590     if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2591       return;
2592     }
2593     if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
2594       return;
2595     }
2596   }
2597   if (!mBundledFontsPath.IsEmpty()) {
2598     FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
2599   }
2600 }
2601 #endif
2602 
2603 #ifdef MOZ_WIDGET_GTK
2604 /***************************************************************************
2605  *
2606  * These functions must be last in the file because it uses the system cairo
2607  * library.  Above this point the cairo library used is the tree cairo if
2608  * MOZ_TREE_CAIRO.
2609  */
2610 
2611 #  if MOZ_TREE_CAIRO
2612 // Tree cairo symbols have different names.  Disable their activation through
2613 // preprocessor macros.
2614 #    undef cairo_ft_font_options_substitute
2615 
2616 #    undef cairo_font_options_create
2617 #    undef cairo_font_options_destroy
2618 #    undef cairo_font_options_copy
2619 #    undef cairo_font_options_equal
2620 
2621 #    undef cairo_font_options_get_antialias
2622 #    undef cairo_font_options_set_antialias
2623 #    undef cairo_font_options_get_hint_style
2624 #    undef cairo_font_options_set_hint_style
2625 #    undef cairo_font_options_get_lcd_filter
2626 #    undef cairo_font_options_set_lcd_filter
2627 #    undef cairo_font_options_get_subpixel_order
2628 #    undef cairo_font_options_set_subpixel_order
2629 
2630 // The system cairo functions are not declared because the include paths cause
2631 // the gdk headers to pick up the tree cairo.h.
2632 extern "C" {
2633 NS_VISIBILITY_DEFAULT void cairo_ft_font_options_substitute(
2634     const cairo_font_options_t* options, FcPattern* pattern);
2635 
2636 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_copy(
2637     const cairo_font_options_t*);
2638 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_create();
2639 NS_VISIBILITY_DEFAULT void cairo_font_options_destroy(cairo_font_options_t*);
2640 NS_VISIBILITY_DEFAULT cairo_bool_t cairo_font_options_equal(
2641     const cairo_font_options_t*, const cairo_font_options_t*);
2642 
2643 NS_VISIBILITY_DEFAULT cairo_antialias_t
2644 cairo_font_options_get_antialias(const cairo_font_options_t*);
2645 NS_VISIBILITY_DEFAULT void cairo_font_options_set_antialias(
2646     cairo_font_options_t*, cairo_antialias_t);
2647 NS_VISIBILITY_DEFAULT cairo_hint_style_t
2648 cairo_font_options_get_hint_style(const cairo_font_options_t*);
2649 NS_VISIBILITY_DEFAULT void cairo_font_options_set_hint_style(
2650     cairo_font_options_t*, cairo_hint_style_t);
2651 NS_VISIBILITY_DEFAULT cairo_subpixel_order_t
2652 cairo_font_options_get_subpixel_order(const cairo_font_options_t*);
2653 NS_VISIBILITY_DEFAULT void cairo_font_options_set_subpixel_order(
2654     cairo_font_options_t*, cairo_subpixel_order_t);
2655 }
2656 #  endif
2657 
ClearSystemFontOptions()2658 void gfxFcPlatformFontList::ClearSystemFontOptions() {
2659   if (mSystemFontOptions) {
2660     cairo_font_options_destroy(mSystemFontOptions);
2661     mSystemFontOptions = nullptr;
2662   }
2663 }
2664 
UpdateSystemFontOptions()2665 bool gfxFcPlatformFontList::UpdateSystemFontOptions() {
2666   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
2667 
2668   if (gfxPlatform::IsHeadless()) {
2669     return false;
2670   }
2671 
2672 #  ifdef MOZ_X11
2673   {
2674     // This one shouldn't change during the X session.
2675     int lcdfilter;
2676     GdkDisplay* dpy = gdk_display_get_default();
2677     if (mozilla::widget::GdkIsX11Display(dpy) &&
2678         GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
2679       mFreetypeLcdSetting = lcdfilter;
2680     }
2681   }
2682 #  endif  // MOZ_X11
2683 
2684   const cairo_font_options_t* options =
2685       gdk_screen_get_font_options(gdk_screen_get_default());
2686   if (!options) {
2687     bool changed = !!mSystemFontOptions;
2688     ClearSystemFontOptions();
2689     return changed;
2690   }
2691 
2692   cairo_font_options_t* newOptions = cairo_font_options_copy(options);
2693 
2694   if (mSystemFontOptions &&
2695       cairo_font_options_equal(mSystemFontOptions, options)) {
2696     cairo_font_options_destroy(newOptions);
2697     return false;
2698   }
2699 
2700   ClearSystemFontOptions();
2701   mSystemFontOptions = newOptions;
2702   return true;
2703 }
2704 
SystemFontOptionsToIpc(dom::SystemFontOptions & aOptions)2705 void gfxFcPlatformFontList::SystemFontOptionsToIpc(
2706     dom::SystemFontOptions& aOptions) {
2707   aOptions.antialias() =
2708       mSystemFontOptions ? cairo_font_options_get_antialias(mSystemFontOptions)
2709                          : CAIRO_ANTIALIAS_DEFAULT;
2710   aOptions.subpixelOrder() =
2711       mSystemFontOptions
2712           ? cairo_font_options_get_subpixel_order(mSystemFontOptions)
2713           : CAIRO_SUBPIXEL_ORDER_DEFAULT;
2714   aOptions.hintStyle() =
2715       mSystemFontOptions ? cairo_font_options_get_hint_style(mSystemFontOptions)
2716                          : CAIRO_HINT_STYLE_DEFAULT;
2717   aOptions.lcdFilter() = mFreetypeLcdSetting;
2718 }
2719 
UpdateSystemFontOptionsFromIpc(const dom::SystemFontOptions & aOptions)2720 void gfxFcPlatformFontList::UpdateSystemFontOptionsFromIpc(
2721     const dom::SystemFontOptions& aOptions) {
2722   ClearSystemFontOptions();
2723   mSystemFontOptions = cairo_font_options_create();
2724   cairo_font_options_set_antialias(mSystemFontOptions,
2725                                    cairo_antialias_t(aOptions.antialias()));
2726   cairo_font_options_set_hint_style(mSystemFontOptions,
2727                                     cairo_hint_style_t(aOptions.hintStyle()));
2728   cairo_font_options_set_subpixel_order(
2729       mSystemFontOptions, cairo_subpixel_order_t(aOptions.subpixelOrder()));
2730   mFreetypeLcdSetting = aOptions.lcdFilter();
2731 }
2732 
SubstituteSystemFontOptions(FcPattern * aPattern)2733 void gfxFcPlatformFontList::SubstituteSystemFontOptions(FcPattern* aPattern) {
2734   if (mSystemFontOptions) {
2735     cairo_ft_font_options_substitute(mSystemFontOptions, aPattern);
2736   }
2737 
2738   if (mFreetypeLcdSetting != -1) {
2739     FcValue value;
2740     if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
2741       FcPatternAddInteger(aPattern, FC_LCD_FILTER, mFreetypeLcdSetting);
2742     }
2743   }
2744 }
2745 
2746 #endif  // MOZ_WIDGET_GTK
2747