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/ArrayUtils.h"
7 #include "mozilla/FontPropertyTypes.h"
8 #include "mozilla/MemoryReporting.h"
9 #include "mozilla/intl/OSPreferences.h"
10 
11 #include "gfxDWriteFontList.h"
12 #include "gfxDWriteFonts.h"
13 #include "nsUnicharUtils.h"
14 #include "nsServiceManagerUtils.h"
15 #include "nsCharSeparatedTokenizer.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/ProfilerLabels.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/StaticPrefs_gfx.h"
20 #include "mozilla/Telemetry.h"
21 #include "mozilla/WindowsProcessMitigations.h"
22 #include "mozilla/WindowsVersion.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 
27 #include "gfxGDIFontList.h"
28 
29 #include "harfbuzz/hb.h"
30 
31 #include "StandardFonts-win10.inc"
32 
33 using namespace mozilla;
34 using namespace mozilla::gfx;
35 using mozilla::intl::OSPreferences;
36 
37 #define LOG_FONTLIST(args) \
38   MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
39 #define LOG_FONTLIST_ENABLED() \
40   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
41 
42 #define LOG_FONTINIT(args) \
43   MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
44 #define LOG_FONTINIT_ENABLED() \
45   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
46 
47 #define LOG_CMAPDATA_ENABLED() \
48   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
49 
BuildKeyNameFromFontName(nsACString & aName)50 static __inline void BuildKeyNameFromFontName(nsACString& aName) {
51   ToLowerCase(aName);
52 }
53 
54 ////////////////////////////////////////////////////////////////////////////////
55 // gfxDWriteFontFamily
56 
~gfxDWriteFontFamily()57 gfxDWriteFontFamily::~gfxDWriteFontFamily() {}
58 
GetNameAsUtf8(nsACString & aName,IDWriteLocalizedStrings * aStrings,UINT32 aIndex)59 static bool GetNameAsUtf8(nsACString& aName, IDWriteLocalizedStrings* aStrings,
60                           UINT32 aIndex) {
61   AutoTArray<WCHAR, 32> name;
62   UINT32 length;
63   HRESULT hr = aStrings->GetStringLength(aIndex, &length);
64   if (FAILED(hr)) {
65     return false;
66   }
67   if (!name.SetLength(length + 1, fallible)) {
68     return false;
69   }
70   hr = aStrings->GetString(aIndex, name.Elements(), length + 1);
71   if (FAILED(hr)) {
72     return false;
73   }
74   aName.Truncate();
75   AppendUTF16toUTF8(
76       Substring(reinterpret_cast<const char16_t*>(name.Elements()),
77                 name.Length() - 1),
78       aName);
79   return true;
80 }
81 
GetEnglishOrFirstName(nsACString & aName,IDWriteLocalizedStrings * aStrings)82 static bool GetEnglishOrFirstName(nsACString& aName,
83                                   IDWriteLocalizedStrings* aStrings) {
84   UINT32 englishIdx = 0;
85   BOOL exists;
86   HRESULT hr = aStrings->FindLocaleName(L"en-us", &englishIdx, &exists);
87   if (FAILED(hr) || !exists) {
88     // Use 0 index if english is not found.
89     englishIdx = 0;
90   }
91   return GetNameAsUtf8(aName, aStrings, englishIdx);
92 }
93 
GetDirectWriteFontName(IDWriteFont * aFont,nsACString & aFontName)94 static HRESULT GetDirectWriteFontName(IDWriteFont* aFont,
95                                       nsACString& aFontName) {
96   HRESULT hr;
97 
98   RefPtr<IDWriteLocalizedStrings> names;
99   hr = aFont->GetFaceNames(getter_AddRefs(names));
100   if (FAILED(hr)) {
101     return hr;
102   }
103 
104   if (!GetEnglishOrFirstName(aFontName, names)) {
105     return E_FAIL;
106   }
107 
108   return S_OK;
109 }
110 
111 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME
112 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME
113 
114 // for use in reading postscript or fullname
GetDirectWriteFaceName(IDWriteFont * aFont,DWRITE_INFORMATIONAL_STRING_ID aWhichName,nsACString & aFontName)115 static HRESULT GetDirectWriteFaceName(IDWriteFont* aFont,
116                                       DWRITE_INFORMATIONAL_STRING_ID aWhichName,
117                                       nsACString& aFontName) {
118   HRESULT hr;
119 
120   BOOL exists;
121   RefPtr<IDWriteLocalizedStrings> infostrings;
122   hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings),
123                                       &exists);
124   if (FAILED(hr) || !exists) {
125     return E_FAIL;
126   }
127 
128   if (!GetEnglishOrFirstName(aFontName, infostrings)) {
129     return E_FAIL;
130   }
131 
132   return S_OK;
133 }
134 
FindStyleVariations(FontInfoData * aFontInfoData)135 void gfxDWriteFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
136   HRESULT hr;
137   if (mHasStyles) {
138     return;
139   }
140   mHasStyles = true;
141 
142   gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
143 
144   bool skipFaceNames =
145       mFaceNamesInitialized || !fp->NeedFullnamePostscriptNames();
146   bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
147                                      fp->NeedFullnamePostscriptNames() &&
148                                      aFontInfoData;
149 
150   for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
151     RefPtr<IDWriteFont> font;
152     hr = mDWFamily->GetFont(i, getter_AddRefs(font));
153     if (FAILED(hr)) {
154       // This should never happen.
155       NS_WARNING("Failed to get existing font from family.");
156       continue;
157     }
158 
159     if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
160       // We don't want these in the font list; we'll apply simulations
161       // on the fly when appropriate.
162       continue;
163     }
164 
165     // name
166     nsCString fullID(mName);
167     nsAutoCString faceName;
168     hr = GetDirectWriteFontName(font, faceName);
169     if (FAILED(hr)) {
170       continue;
171     }
172     fullID.Append(' ');
173     fullID.Append(faceName);
174 
175     // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
176     // non-italic style glyphs as Japanese characters.  However, using it
177     // causes serious problem if web pages wants some elements to be
178     // different style from others only with font-style.  For example,
179     // <em> and <i> should be rendered as italic in the default style.
180     if (fullID.EqualsLiteral("Meiryo Italic") ||
181         fullID.EqualsLiteral("Meiryo Bold Italic")) {
182       continue;
183     }
184 
185     gfxDWriteFontEntry* fe =
186         new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
187     fe->SetForceGDIClassic(mForceGDIClassic);
188 
189     fe->SetupVariationRanges();
190 
191     AddFontEntry(fe);
192 
193     // postscript/fullname if needed
194     nsAutoCString psname, fullname;
195     if (fontInfoShouldHaveFaceNames) {
196       aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
197       if (!fullname.IsEmpty()) {
198         fp->AddFullname(fe, fullname);
199       }
200       if (!psname.IsEmpty()) {
201         fp->AddPostscriptName(fe, psname);
202       }
203     } else if (!skipFaceNames) {
204       hr = GetDirectWriteFaceName(font, PSNAME_ID, psname);
205       if (FAILED(hr)) {
206         skipFaceNames = true;
207       } else if (psname.Length() > 0) {
208         fp->AddPostscriptName(fe, psname);
209       }
210 
211       hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname);
212       if (FAILED(hr)) {
213         skipFaceNames = true;
214       } else if (fullname.Length() > 0) {
215         fp->AddFullname(fe, fullname);
216       }
217     }
218 
219     if (LOG_FONTLIST_ENABLED()) {
220       nsAutoCString weightString;
221       fe->Weight().ToString(weightString);
222       LOG_FONTLIST(
223           ("(fontlist) added (%s) to family (%s)"
224            " with style: %s weight: %s stretch: %d psname: %s fullname: %s",
225            fe->Name().get(), Name().get(),
226            (fe->IsItalic()) ? "italic"
227                             : (fe->IsOblique() ? "oblique" : "normal"),
228            weightString.get(), fe->Stretch(), psname.get(), fullname.get()));
229     }
230   }
231 
232   // assume that if no error, all postscript/fullnames were initialized
233   if (!skipFaceNames) {
234     mFaceNamesInitialized = true;
235   }
236 
237   if (!mAvailableFonts.Length()) {
238     NS_WARNING("Family with no font faces in it.");
239   }
240 
241   if (mIsBadUnderlineFamily) {
242     SetBadUnderlineFonts();
243   }
244 
245   CheckForSimpleFamily();
246   if (mIsSimpleFamily) {
247     for (auto& f : mAvailableFonts) {
248       if (f) {
249         static_cast<gfxDWriteFontEntry*>(f.get())->mMayUseGDIAccess = true;
250       }
251     }
252   }
253 }
254 
ReadFaceNames(gfxPlatformFontList * aPlatformFontList,bool aNeedFullnamePostscriptNames,FontInfoData * aFontInfoData)255 void gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
256                                         bool aNeedFullnamePostscriptNames,
257                                         FontInfoData* aFontInfoData) {
258   // if all needed names have already been read, skip
259   if (mOtherFamilyNamesInitialized &&
260       (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
261     return;
262   }
263 
264   // If we've been passed a FontInfoData, we skip the DWrite implementation
265   // here and fall back to the generic code which will use that info.
266   if (!aFontInfoData) {
267     // DirectWrite version of this will try to read
268     // postscript/fullnames via DirectWrite API
269     FindStyleVariations();
270   }
271 
272   // fallback to looking up via name table
273   if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) {
274     gfxFontFamily::ReadFaceNames(aPlatformFontList,
275                                  aNeedFullnamePostscriptNames, aFontInfoData);
276   }
277 }
278 
LocalizedName(nsACString & aLocalizedName)279 void gfxDWriteFontFamily::LocalizedName(nsACString& aLocalizedName) {
280   aLocalizedName = Name();  // just return canonical name in case of failure
281 
282   if (!mDWFamily) {
283     return;
284   }
285 
286   HRESULT hr;
287   nsAutoCString locale;
288   // We use system locale here because it's what user expects to see.
289   // See bug 1349454 for details.
290   RefPtr<OSPreferences> osprefs = OSPreferences::GetInstanceAddRefed();
291   if (!osprefs) {
292     return;
293   }
294   osprefs->GetSystemLocale(locale);
295 
296   RefPtr<IDWriteLocalizedStrings> names;
297 
298   hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
299   if (FAILED(hr)) {
300     return;
301   }
302   UINT32 idx = 0;
303   BOOL exists;
304   hr =
305       names->FindLocaleName(NS_ConvertUTF8toUTF16(locale).get(), &idx, &exists);
306   if (FAILED(hr)) {
307     return;
308   }
309   if (!exists) {
310     // Use english is localized is not found.
311     hr = names->FindLocaleName(L"en-us", &idx, &exists);
312     if (FAILED(hr)) {
313       return;
314     }
315     if (!exists) {
316       // Use 0 index if english is not found.
317       idx = 0;
318     }
319   }
320   AutoTArray<WCHAR, 32> famName;
321   UINT32 length;
322 
323   hr = names->GetStringLength(idx, &length);
324   if (FAILED(hr)) {
325     return;
326   }
327 
328   if (!famName.SetLength(length + 1, fallible)) {
329     // Eeep - running out of memory. Unlikely to end well.
330     return;
331   }
332 
333   hr = names->GetString(idx, famName.Elements(), length + 1);
334   if (FAILED(hr)) {
335     return;
336   }
337 
338   aLocalizedName = NS_ConvertUTF16toUTF8((const char16_t*)famName.Elements(),
339                                          famName.Length() - 1);
340 }
341 
IsSymbolFontFamily() const342 bool gfxDWriteFontFamily::IsSymbolFontFamily() const {
343   // Just check the first font in the family
344   if (mDWFamily->GetFontCount() > 0) {
345     RefPtr<IDWriteFont> font;
346     if (SUCCEEDED(mDWFamily->GetFont(0, getter_AddRefs(font)))) {
347       return font->IsSymbolFont();
348     }
349   }
350   return false;
351 }
352 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const353 void gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
354                                                  FontListSizes* aSizes) const {
355   gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
356   // TODO:
357   // This doesn't currently account for |mDWFamily|
358 }
359 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const360 void gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
361                                                  FontListSizes* aSizes) const {
362   aSizes->mFontListSize += aMallocSizeOf(this);
363   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
364 }
365 
366 ////////////////////////////////////////////////////////////////////////////////
367 // gfxDWriteFontEntry
368 
Clone() const369 gfxFontEntry* gfxDWriteFontEntry::Clone() const {
370   MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
371   gfxDWriteFontEntry* fe = new gfxDWriteFontEntry(Name(), mFont);
372   fe->mWeightRange = mWeightRange;
373   fe->mStretchRange = mStretchRange;
374   fe->mStyleRange = mStyleRange;
375   return fe;
376 }
377 
~gfxDWriteFontEntry()378 gfxDWriteFontEntry::~gfxDWriteFontEntry() {}
379 
UsingArabicOrHebrewScriptSystemLocale()380 static bool UsingArabicOrHebrewScriptSystemLocale() {
381   LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID());
382   switch (langid) {
383     case LANG_ARABIC:
384     case LANG_DARI:
385     case LANG_PASHTO:
386     case LANG_PERSIAN:
387     case LANG_SINDHI:
388     case LANG_UIGHUR:
389     case LANG_URDU:
390     case LANG_HEBREW:
391       return true;
392     default:
393       return false;
394   }
395 }
396 
CopyFontTable(uint32_t aTableTag,nsTArray<uint8_t> & aBuffer)397 nsresult gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag,
398                                            nsTArray<uint8_t>& aBuffer) {
399   gfxDWriteFontList* pFontList = gfxDWriteFontList::PlatformFontList();
400   const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag);
401 
402   // Don't use GDI table loading for symbol fonts or for
403   // italic fonts in Arabic-script system locales because of
404   // potential cmap discrepancies, see bug 629386.
405   // Ditto for Hebrew, bug 837498.
406   if (mFont && mMayUseGDIAccess && pFontList->UseGDIFontTableAccess() &&
407       !(!IsUpright() && UsingArabicOrHebrewScriptSystemLocale()) &&
408       !mFont->IsSymbolFont()) {
409     LOGFONTW logfont = {0};
410     if (InitLogFont(mFont, &logfont)) {
411       AutoDC dc;
412       AutoSelectFont font(dc.GetDC(), &logfont);
413       if (font.IsValid()) {
414         uint32_t tableSize = ::GetFontData(dc.GetDC(), tagBE, 0, nullptr, 0);
415         if (tableSize != GDI_ERROR) {
416           if (aBuffer.SetLength(tableSize, fallible)) {
417             ::GetFontData(dc.GetDC(), tagBE, 0, aBuffer.Elements(),
418                           aBuffer.Length());
419             return NS_OK;
420           }
421           return NS_ERROR_OUT_OF_MEMORY;
422         }
423       }
424     }
425   }
426 
427   RefPtr<IDWriteFontFace> fontFace;
428   nsresult rv = CreateFontFace(getter_AddRefs(fontFace));
429   if (NS_FAILED(rv)) {
430     return rv;
431   }
432 
433   uint8_t* tableData;
434   uint32_t len;
435   void* tableContext = nullptr;
436   BOOL exists;
437   HRESULT hr = fontFace->TryGetFontTable(tagBE, (const void**)&tableData, &len,
438                                          &tableContext, &exists);
439   if (FAILED(hr) || !exists) {
440     return NS_ERROR_FAILURE;
441   }
442 
443   if (aBuffer.SetLength(len, fallible)) {
444     memcpy(aBuffer.Elements(), tableData, len);
445     rv = NS_OK;
446   } else {
447     rv = NS_ERROR_OUT_OF_MEMORY;
448   }
449 
450   if (tableContext) {
451     fontFace->ReleaseFontTable(&tableContext);
452   }
453 
454   return rv;
455 }
456 
457 // Access to font tables packaged in hb_blob_t form
458 
459 // object attached to the Harfbuzz blob, used to release
460 // the table when the blob is destroyed
461 class FontTableRec {
462  public:
FontTableRec(IDWriteFontFace * aFontFace,void * aContext)463   FontTableRec(IDWriteFontFace* aFontFace, void* aContext)
464       : mFontFace(aFontFace), mContext(aContext) {
465     MOZ_COUNT_CTOR(FontTableRec);
466   }
467 
~FontTableRec()468   ~FontTableRec() {
469     MOZ_COUNT_DTOR(FontTableRec);
470     mFontFace->ReleaseFontTable(mContext);
471   }
472 
473  private:
474   RefPtr<IDWriteFontFace> mFontFace;
475   void* mContext;
476 };
477 
DestroyBlobFunc(void * aUserData)478 static void DestroyBlobFunc(void* aUserData) {
479   FontTableRec* ftr = static_cast<FontTableRec*>(aUserData);
480   delete ftr;
481 }
482 
GetFontTable(uint32_t aTag)483 hb_blob_t* gfxDWriteFontEntry::GetFontTable(uint32_t aTag) {
484   // try to avoid potentially expensive DWrite call if we haven't actually
485   // created the font face yet, by using the gfxFontEntry method that will
486   // use CopyFontTable and then cache the data
487   if (!mFontFace) {
488     return gfxFontEntry::GetFontTable(aTag);
489   }
490 
491   const void* data;
492   UINT32 size;
493   void* context;
494   BOOL exists;
495   HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag),
496                                           &data, &size, &context, &exists);
497   if (SUCCEEDED(hr) && exists) {
498     FontTableRec* ftr = new FontTableRec(mFontFace, context);
499     return hb_blob_create(static_cast<const char*>(data), size,
500                           HB_MEMORY_MODE_READONLY, ftr, DestroyBlobFunc);
501   }
502 
503   return nullptr;
504 }
505 
ReadCMAP(FontInfoData * aFontInfoData)506 nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
507   AUTO_PROFILER_LABEL("gfxDWriteFontEntry::ReadCMAP", GRAPHICS);
508 
509   // attempt this once, if errors occur leave a blank cmap
510   if (mCharacterMap || mShmemCharacterMap) {
511     return NS_OK;
512   }
513 
514   RefPtr<gfxCharacterMap> charmap;
515   nsresult rv;
516 
517   if (aFontInfoData &&
518       (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
519     rv = NS_OK;
520   } else {
521     uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
522     charmap = new gfxCharacterMap();
523     AutoTable cmapTable(this, kCMAP);
524 
525     if (cmapTable) {
526       uint32_t cmapLen;
527       const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
528           hb_blob_get_data(cmapTable, &cmapLen));
529       rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, mUVSOffset);
530     } else {
531       rv = NS_ERROR_NOT_AVAILABLE;
532     }
533   }
534 
535   mHasCmapTable = NS_SUCCEEDED(rv);
536   if (mHasCmapTable) {
537     // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used
538     // by sites to represent a "Play" icon, but the glyph in Segoe UI Light
539     // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.)
540     // Fallback to Segoe UI Symbol is preferred.
541     if (FamilyName().EqualsLiteral("Segoe UI")) {
542       charmap->clear(0x25b6);
543       charmap->clear(0x25c0);
544     }
545     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
546     fontlist::FontList* sharedFontList = pfl->SharedFontList();
547     if (!IsUserFont() && mShmemFace) {
548       mShmemFace->SetCharacterMap(sharedFontList, charmap);  // async
549       if (!TrySetShmemCharacterMap()) {
550         // Temporarily retain charmap, until the shared version is
551         // ready for use.
552         mCharacterMap = charmap;
553       }
554     } else {
555       mCharacterMap = pfl->FindCharMap(charmap);
556     }
557   } else {
558     // if error occurred, initialize to null cmap
559     mCharacterMap = new gfxCharacterMap();
560   }
561 
562   LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
563                 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
564                 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
565   if (LOG_CMAPDATA_ENABLED()) {
566     char prefix[256];
567     SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
568     charmap->Dump(prefix, eGfxLog_cmapdata);
569   }
570 
571   return rv;
572 }
573 
HasVariations()574 bool gfxDWriteFontEntry::HasVariations() {
575   if (mHasVariationsInitialized) {
576     return mHasVariations;
577   }
578   mHasVariationsInitialized = true;
579   mHasVariations = false;
580 
581   if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
582     return mHasVariations;
583   }
584 
585   if (!mFontFace) {
586     // CreateFontFace will initialize the mFontFace field, and also
587     // mFontFace5 if available on the current DWrite version.
588     RefPtr<IDWriteFontFace> fontFace;
589     if (NS_FAILED(CreateFontFace(getter_AddRefs(fontFace)))) {
590       return mHasVariations;
591     }
592   }
593   if (mFontFace5) {
594     mHasVariations = mFontFace5->HasVariations();
595   }
596   return mHasVariations;
597 }
598 
GetVariationAxes(nsTArray<gfxFontVariationAxis> & aAxes)599 void gfxDWriteFontEntry::GetVariationAxes(
600     nsTArray<gfxFontVariationAxis>& aAxes) {
601   if (!HasVariations()) {
602     return;
603   }
604   // HasVariations() will have ensured the mFontFace5 interface is available;
605   // so we can get an IDWriteFontResource and ask it for the axis info.
606   RefPtr<IDWriteFontResource> resource;
607   HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
608   if (FAILED(hr) || !resource) {
609     return;
610   }
611 
612   uint32_t count = resource->GetFontAxisCount();
613   AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> defaultValues;
614   AutoTArray<DWRITE_FONT_AXIS_RANGE, 4> ranges;
615   defaultValues.SetLength(count);
616   ranges.SetLength(count);
617   resource->GetDefaultFontAxisValues(defaultValues.Elements(), count);
618   resource->GetFontAxisRanges(ranges.Elements(), count);
619   for (uint32_t i = 0; i < count; ++i) {
620     gfxFontVariationAxis axis;
621     MOZ_ASSERT(ranges[i].axisTag == defaultValues[i].axisTag);
622     DWRITE_FONT_AXIS_ATTRIBUTES attrs = resource->GetFontAxisAttributes(i);
623     if (attrs & DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN) {
624       continue;
625     }
626     if (!(attrs & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE)) {
627       continue;
628     }
629     // Extract the 4 chars of the tag from DWrite's packed version,
630     // and reassemble them in the order we use for TRUETYPE_TAG.
631     uint32_t t = defaultValues[i].axisTag;
632     axis.mTag = TRUETYPE_TAG(t & 0xff, (t >> 8) & 0xff, (t >> 16) & 0xff,
633                              (t >> 24) & 0xff);
634     // Try to get a human-friendly name (may not be present)
635     RefPtr<IDWriteLocalizedStrings> names;
636     resource->GetAxisNames(i, getter_AddRefs(names));
637     if (names) {
638       GetEnglishOrFirstName(axis.mName, names);
639     }
640     axis.mMinValue = ranges[i].minValue;
641     axis.mMaxValue = ranges[i].maxValue;
642     axis.mDefaultValue = defaultValues[i].value;
643     aAxes.AppendElement(axis);
644   }
645 }
646 
GetVariationInstances(nsTArray<gfxFontVariationInstance> & aInstances)647 void gfxDWriteFontEntry::GetVariationInstances(
648     nsTArray<gfxFontVariationInstance>& aInstances) {
649   gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
650 }
651 
CreateFontInstance(const gfxFontStyle * aFontStyle)652 gfxFont* gfxDWriteFontEntry::CreateFontInstance(
653     const gfxFontStyle* aFontStyle) {
654   bool needsBold = aFontStyle->NeedsSyntheticBold(this);
655   DWRITE_FONT_SIMULATIONS sims =
656       needsBold ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
657   ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
658       needsBold ? mUnscaledFontBold : mUnscaledFont;
659   RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
660   if (!unscaledFont) {
661     RefPtr<IDWriteFontFace> fontFace;
662     nsresult rv =
663         CreateFontFace(getter_AddRefs(fontFace), nullptr, sims, nullptr);
664     if (NS_FAILED(rv)) {
665       return nullptr;
666     }
667     // Only pass in the underlying IDWriteFont if the unscaled font doesn't
668     // reflect a data font. This signals whether or not we can safely query
669     // a descriptor to represent the font for various transport use-cases.
670     unscaledFont =
671         new UnscaledFontDWrite(fontFace, !mIsDataUserFont ? mFont : nullptr);
672     unscaledFontPtr = unscaledFont;
673   }
674   RefPtr<IDWriteFontFace> fontFace;
675   if (HasVariations()) {
676     // Get the variation settings needed to instantiate the fontEntry
677     // for a particular fontStyle.
678     AutoTArray<gfxFontVariation, 4> vars;
679     GetVariationsForStyle(vars, *aFontStyle);
680 
681     if (!vars.IsEmpty()) {
682       nsresult rv =
683           CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims, &vars);
684       if (NS_FAILED(rv)) {
685         return nullptr;
686       }
687     }
688   }
689   return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace);
690 }
691 
CreateFontFace(IDWriteFontFace ** aFontFace,const gfxFontStyle * aFontStyle,DWRITE_FONT_SIMULATIONS aSimulations,const nsTArray<gfxFontVariation> * aVariations)692 nsresult gfxDWriteFontEntry::CreateFontFace(
693     IDWriteFontFace** aFontFace, const gfxFontStyle* aFontStyle,
694     DWRITE_FONT_SIMULATIONS aSimulations,
695     const nsTArray<gfxFontVariation>* aVariations) {
696   // Convert an OpenType font tag from our uint32_t representation
697   // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants.
698   auto makeDWriteAxisTag = [](uint32_t aTag) {
699     return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff, (aTag >> 16) & 0xff,
700                                      (aTag >> 8) & 0xff, aTag & 0xff);
701   };
702 
703   // initialize mFontFace if this hasn't been done before
704   if (!mFontFace) {
705     HRESULT hr;
706     if (mFont) {
707       hr = mFont->CreateFontFace(getter_AddRefs(mFontFace));
708     } else if (mFontFile) {
709       IDWriteFontFile* fontFile = mFontFile.get();
710       hr = Factory::GetDWriteFactory()->CreateFontFace(
711           mFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE,
712           getter_AddRefs(mFontFace));
713     } else {
714       MOZ_ASSERT_UNREACHABLE("invalid font entry");
715       return NS_ERROR_FAILURE;
716     }
717     if (FAILED(hr)) {
718       return NS_ERROR_FAILURE;
719     }
720     // Also get the IDWriteFontFace5 interface if we're running on a
721     // sufficiently new DWrite version where it is available.
722     if (mFontFace) {
723       mFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
724                                 (void**)getter_AddRefs(mFontFace5));
725       if (!mVariationSettings.IsEmpty()) {
726         // If the font entry has variations specified, mFontFace5 will
727         // be a distinct face that has the variations applied.
728         RefPtr<IDWriteFontResource> resource;
729         HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
730         if (SUCCEEDED(hr) && resource) {
731           AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
732           for (const auto& v : mVariationSettings) {
733             DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
734                                                 v.mValue};
735             fontAxisValues.AppendElement(axisValue);
736           }
737           resource->CreateFontFace(
738               mFontFace->GetSimulations(), fontAxisValues.Elements(),
739               fontAxisValues.Length(), getter_AddRefs(mFontFace5));
740         }
741       }
742     }
743   }
744 
745   // Do we need to modify DWrite simulations from what mFontFace has?
746   bool needSimulations =
747       (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
748       !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
749 
750   // If the IDWriteFontFace5 interface is available, we can try using
751   // IDWriteFontResource to create a new modified face.
752   if (mFontFace5 && (HasVariations() || needSimulations)) {
753     RefPtr<IDWriteFontResource> resource;
754     HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
755     if (SUCCEEDED(hr) && resource) {
756       AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
757 
758       // Copy variation settings to DWrite's type.
759       if (aVariations) {
760         for (const auto& v : *aVariations) {
761           DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
762                                               v.mValue};
763           fontAxisValues.AppendElement(axisValue);
764         }
765       }
766 
767       IDWriteFontFace5* ff5;
768       resource->CreateFontFace(aSimulations, fontAxisValues.Elements(),
769                                fontAxisValues.Length(), &ff5);
770       if (ff5) {
771         *aFontFace = ff5;
772         return NS_OK;
773       }
774     }
775   }
776 
777   // Do we need to add DWrite simulations to the face?
778   if (needSimulations) {
779     // if so, we need to return not mFontFace itself but a version that
780     // has the Bold simulation - unfortunately, old DWrite doesn't provide
781     // a simple API for this
782     UINT32 numberOfFiles = 0;
783     if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) {
784       return NS_ERROR_FAILURE;
785     }
786     AutoTArray<IDWriteFontFile*, 1> files;
787     files.AppendElements(numberOfFiles);
788     if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
789       return NS_ERROR_FAILURE;
790     }
791     HRESULT hr = Factory::GetDWriteFactory()->CreateFontFace(
792         mFontFace->GetType(), numberOfFiles, files.Elements(),
793         mFontFace->GetIndex(), aSimulations, aFontFace);
794     for (UINT32 i = 0; i < numberOfFiles; ++i) {
795       files[i]->Release();
796     }
797     return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
798   }
799 
800   // no simulation: we can just add a reference to mFontFace5 (if present)
801   // or mFontFace (otherwise) and return that
802   if (mFontFace5) {
803     *aFontFace = mFontFace5;
804   } else {
805     *aFontFace = mFontFace;
806   }
807   (*aFontFace)->AddRef();
808   return NS_OK;
809 }
810 
InitLogFont(IDWriteFont * aFont,LOGFONTW * aLogFont)811 bool gfxDWriteFontEntry::InitLogFont(IDWriteFont* aFont, LOGFONTW* aLogFont) {
812   HRESULT hr;
813 
814   BOOL isInSystemCollection;
815   IDWriteGdiInterop* gdi =
816       gfxDWriteFontList::PlatformFontList()->GetGDIInterop();
817   hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection);
818   // If the font is not in the system collection, GDI will be unable to
819   // select it and load its tables, so we return false here to indicate
820   // failure, and let CopyFontTable fall back to DWrite native methods.
821   return (SUCCEEDED(hr) && isInSystemCollection);
822 }
823 
IsCJKFont()824 bool gfxDWriteFontEntry::IsCJKFont() {
825   if (mIsCJK != UNINITIALIZED_VALUE) {
826     return mIsCJK;
827   }
828 
829   mIsCJK = false;
830 
831   const uint32_t kOS2Tag = TRUETYPE_TAG('O', 'S', '/', '2');
832   hb_blob_t* blob = GetFontTable(kOS2Tag);
833   if (!blob) {
834     return mIsCJK;
835   }
836   // |blob| is an owning reference, but is not RAII-managed, so it must be
837   // explicitly freed using |hb_blob_destroy| before we return. (Beware of
838   // adding any early-return codepaths!)
839 
840   uint32_t len;
841   const OS2Table* os2 =
842       reinterpret_cast<const OS2Table*>(hb_blob_get_data(blob, &len));
843   // ulCodePageRange bit definitions for the CJK codepages,
844   // from http://www.microsoft.com/typography/otspec/os2.htm#cpr
845   const uint32_t CJK_CODEPAGE_BITS =
846       (1 << 17) |  // codepage 932 - JIS/Japan
847       (1 << 18) |  // codepage 936 - Chinese (simplified)
848       (1 << 19) |  // codepage 949 - Korean Wansung
849       (1 << 20) |  // codepage 950 - Chinese (traditional)
850       (1 << 21);   // codepage 1361 - Korean Johab
851   if (len >= offsetof(OS2Table, sxHeight)) {
852     if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) {
853       mIsCJK = true;
854     }
855   }
856   hb_blob_destroy(blob);
857 
858   return mIsCJK;
859 }
860 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const861 void gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
862                                                 FontListSizes* aSizes) const {
863   gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
864   // TODO:
865   // This doesn't currently account for the |mFont| and |mFontFile| members
866 }
867 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const868 void gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
869                                                 FontListSizes* aSizes) const {
870   aSizes->mFontListSize += aMallocSizeOf(this);
871   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
872 }
873 
874 ////////////////////////////////////////////////////////////////////////////////
875 // gfxDWriteFontList
876 
gfxDWriteFontList()877 gfxDWriteFontList::gfxDWriteFontList() : mForceGDIClassicMaxFontSize(0.0) {
878   CheckFamilyList(kBaseFonts, ArrayLength(kBaseFonts));
879   CheckFamilyList(kLangPackFonts, ArrayLength(kLangPackFonts));
880 }
881 
882 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
883 //   I/O strain during cold startup due to dwrite caching bugs.  Default to
884 //   Arial to avoid this.
885 
GetDefaultFontForPlatform(const gfxFontStyle * aStyle,nsAtom * aLanguage)886 FontFamily gfxDWriteFontList::GetDefaultFontForPlatform(
887     const gfxFontStyle* aStyle, nsAtom* aLanguage) {
888   // try Arial first
889   FontFamily ff;
890   ff = FindFamily("Arial"_ns);
891   if (!ff.IsNull()) {
892     return ff;
893   }
894 
895   // otherwise, use local default
896   NONCLIENTMETRICSW ncm;
897   ncm.cbSize = sizeof(ncm);
898   BOOL status =
899       ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
900 
901   if (status) {
902     ff = FindFamily(NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
903   }
904 
905   return ff;
906 }
907 
LookupLocalFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry)908 gfxFontEntry* gfxDWriteFontList::LookupLocalFont(
909     const nsACString& aFontName, WeightRange aWeightForEntry,
910     StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
911   if (SharedFontList()) {
912     return LookupInSharedFaceNameList(aFontName, aWeightForEntry,
913                                       aStretchForEntry, aStyleForEntry);
914   }
915 
916   gfxFontEntry* lookup;
917 
918   lookup = LookupInFaceNameLists(aFontName);
919   if (!lookup) {
920     return nullptr;
921   }
922 
923   gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
924   gfxDWriteFontEntry* fe =
925       new gfxDWriteFontEntry(lookup->Name(), dwriteLookup->mFont,
926                              aWeightForEntry, aStretchForEntry, aStyleForEntry);
927   fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
928   return fe;
929 }
930 
MakePlatformFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry,const uint8_t * aFontData,uint32_t aLength)931 gfxFontEntry* gfxDWriteFontList::MakePlatformFont(
932     const nsACString& aFontName, WeightRange aWeightForEntry,
933     StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
934     const uint8_t* aFontData, uint32_t aLength) {
935   RefPtr<IDWriteFontFileStream> fontFileStream;
936   RefPtr<IDWriteFontFile> fontFile;
937   HRESULT hr = gfxDWriteFontFileLoader::CreateCustomFontFile(
938       aFontData, aLength, getter_AddRefs(fontFile),
939       getter_AddRefs(fontFileStream));
940   free((void*)aFontData);
941   NS_ASSERTION(SUCCEEDED(hr), "Failed to create font file reference");
942   if (FAILED(hr)) {
943     return nullptr;
944   }
945 
946   nsAutoString uniqueName;
947   nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
948   NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to make unique user font name");
949   if (NS_FAILED(rv)) {
950     return nullptr;
951   }
952 
953   BOOL isSupported;
954   DWRITE_FONT_FILE_TYPE fileType;
955   UINT32 numFaces;
956 
957   auto entry = MakeUnique<gfxDWriteFontEntry>(
958       NS_ConvertUTF16toUTF8(uniqueName), fontFile, fontFileStream,
959       aWeightForEntry, aStretchForEntry, aStyleForEntry);
960 
961   hr = fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
962   NS_ASSERTION(SUCCEEDED(hr), "IDWriteFontFile::Analyze failed");
963   if (FAILED(hr)) {
964     return nullptr;
965   }
966   NS_ASSERTION(isSupported, "Unsupported font file");
967   if (!isSupported) {
968     return nullptr;
969   }
970   NS_ASSERTION(numFaces == 1, "Font file does not contain exactly 1 face");
971   if (numFaces != 1) {
972     // We don't know how to deal with 0 faces either.
973     return nullptr;
974   }
975 
976   return entry.release();
977 }
978 
UseGDIFontTableAccess() const979 bool gfxDWriteFontList::UseGDIFontTableAccess() const {
980   // Using GDI font table access for DWrite is controlled by a pref, but also we
981   // must be able to make win32k calls.
982   return mGDIFontTableAccess && !IsWin32kLockedDown();
983 }
984 
GetPostScriptNameFromNameTable(IDWriteFontFace * aFace,nsCString & aName)985 static void GetPostScriptNameFromNameTable(IDWriteFontFace* aFace,
986                                            nsCString& aName) {
987   const auto kNAME =
988       NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
989   const char* data;
990   UINT32 size;
991   void* context;
992   BOOL exists;
993   if (SUCCEEDED(aFace->TryGetFontTable(kNAME, (const void**)&data, &size,
994                                        &context, &exists)) &&
995       exists) {
996     if (NS_FAILED(gfxFontUtils::ReadCanonicalName(
997             data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aName))) {
998       aName.Truncate(0);
999     }
1000     aFace->ReleaseFontTable(context);
1001   }
1002 }
1003 
CreateFontEntry(fontlist::Face * aFace,const fontlist::Family * aFamily)1004 gfxFontEntry* gfxDWriteFontList::CreateFontEntry(
1005     fontlist::Face* aFace, const fontlist::Family* aFamily) {
1006   IDWriteFontCollection* collection =
1007 #ifdef MOZ_BUNDLED_FONTS
1008       aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1009 #else
1010       mSystemFonts;
1011 #endif
1012   RefPtr<IDWriteFontFamily> family;
1013   bool foundExpectedFamily = false;
1014   const nsCString& familyName =
1015       aFamily->DisplayName().AsString(SharedFontList());
1016   if (aFamily->Index() < collection->GetFontFamilyCount()) {
1017     HRESULT hr =
1018         collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
1019     // Check that the family name is what we expected; if not, fall back to
1020     // search by name. It's sad we have to do this, but it is possible for
1021     // Windows to have given different versions of the system font collection
1022     // to the parent and child processes.
1023     if (SUCCEEDED(hr) && family) {
1024       RefPtr<IDWriteLocalizedStrings> names;
1025       hr = family->GetFamilyNames(getter_AddRefs(names));
1026       if (SUCCEEDED(hr) && names) {
1027         nsAutoCString name;
1028         if (GetEnglishOrFirstName(name, names)) {
1029           foundExpectedFamily = name.Equals(familyName);
1030         }
1031       }
1032     }
1033   }
1034   if (!foundExpectedFamily) {
1035     // Try to get family by name instead of index (to deal with the case of
1036     // collection mismatch).
1037     UINT32 index;
1038     BOOL exists;
1039     NS_ConvertUTF8toUTF16 name16(familyName);
1040     HRESULT hr = collection->FindFamilyName(
1041         reinterpret_cast<const WCHAR*>(name16.BeginReading()), &index, &exists);
1042     if (FAILED(hr) || !exists || index == UINT_MAX ||
1043         FAILED(collection->GetFontFamily(index, getter_AddRefs(family))) ||
1044         !family) {
1045       return nullptr;
1046     }
1047   }
1048 
1049   // Retrieve the required face by index within the family.
1050   RefPtr<IDWriteFont> font;
1051   if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(font))) || !font) {
1052     return nullptr;
1053   }
1054 
1055   // Retrieve the psName from the font, so we can check we've found the
1056   // expected face.
1057   nsAutoCString psName;
1058   if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
1059     RefPtr<IDWriteFontFace> dwFontFace;
1060     if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1061       GetPostScriptNameFromNameTable(dwFontFace, psName);
1062     }
1063   }
1064 
1065   // If it doesn't match, DirectWrite must have shuffled the order of faces
1066   // returned for the family; search by name as a fallback.
1067   nsCString faceName = aFace->mDescriptor.AsString(SharedFontList());
1068   if (psName != faceName) {
1069     gfxWarning() << "Face name mismatch for index " << aFace->mIndex
1070                  << " in family " << familyName.get() << ": expected "
1071                  << faceName.get() << ", found " << psName.get();
1072     for (uint32_t i = 0; i < family->GetFontCount(); ++i) {
1073       if (i == aFace->mIndex) {
1074         continue;  // this was the face we already tried
1075       }
1076       if (FAILED(family->GetFont(i, getter_AddRefs(font))) || !font) {
1077         return nullptr;  // this font family is broken!
1078       }
1079       if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
1080         RefPtr<IDWriteFontFace> dwFontFace;
1081         if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1082           GetPostScriptNameFromNameTable(dwFontFace, psName);
1083         }
1084       }
1085       if (psName == faceName) {
1086         break;
1087       }
1088     }
1089   }
1090   if (psName != faceName) {
1091     return nullptr;
1092   }
1093 
1094   auto fe = new gfxDWriteFontEntry(faceName, font, !aFamily->IsBundled());
1095   fe->InitializeFrom(aFace, aFamily);
1096   fe->mForceGDIClassic = aFamily->IsForceClassic();
1097   fe->mMayUseGDIAccess = aFamily->IsSimple();
1098   return fe;
1099 }
1100 
GetVisibilityForFamily(const nsACString & aName) const1101 FontVisibility gfxDWriteFontList::GetVisibilityForFamily(
1102     const nsACString& aName) const {
1103   if (FamilyInList(aName, kBaseFonts, ArrayLength(kBaseFonts))) {
1104     return FontVisibility::Base;
1105   }
1106   if (FamilyInList(aName, kLangPackFonts, ArrayLength(kLangPackFonts))) {
1107     return FontVisibility::LangPack;
1108   }
1109   return FontVisibility::User;
1110 }
1111 
AppendFamiliesFromCollection(IDWriteFontCollection * aCollection,nsTArray<fontlist::Family::InitData> & aFamilies,const nsTArray<nsCString> * aForceClassicFams)1112 void gfxDWriteFontList::AppendFamiliesFromCollection(
1113     IDWriteFontCollection* aCollection,
1114     nsTArray<fontlist::Family::InitData>& aFamilies,
1115     const nsTArray<nsCString>* aForceClassicFams) {
1116   auto allFacesUltraBold = [](IDWriteFontFamily* aFamily) -> bool {
1117     for (UINT32 i = 0; i < aFamily->GetFontCount(); i++) {
1118       RefPtr<IDWriteFont> font;
1119       HRESULT hr = aFamily->GetFont(i, getter_AddRefs(font));
1120       if (FAILED(hr)) {
1121         NS_WARNING("Failed to get existing font from family.");
1122         continue;
1123       }
1124       nsAutoCString faceName;
1125       hr = GetDirectWriteFontName(font, faceName);
1126       if (FAILED(hr)) {
1127         continue;
1128       }
1129       if (faceName.Find("Ultra Bold"_ns) == kNotFound) {
1130         return false;
1131       }
1132     }
1133     return true;
1134   };
1135 
1136   nsAutoCString locale;
1137   OSPreferences::GetInstance()->GetSystemLocale(locale);
1138   ToLowerCase(locale);
1139   NS_ConvertUTF8toUTF16 loc16(locale);
1140 
1141   for (unsigned i = 0; i < aCollection->GetFontFamilyCount(); ++i) {
1142     RefPtr<IDWriteFontFamily> family;
1143     aCollection->GetFontFamily(i, getter_AddRefs(family));
1144     RefPtr<IDWriteLocalizedStrings> localizedNames;
1145     HRESULT hr = family->GetFamilyNames(getter_AddRefs(localizedNames));
1146     if (FAILED(hr)) {
1147       gfxWarning() << "Failed to get names for font-family " << i;
1148       continue;
1149     }
1150 
1151     auto addFamily = [&](const nsACString& name, bool altLocale = false) {
1152       nsAutoCString key;
1153       key = name;
1154       BuildKeyNameFromFontName(key);
1155       bool bad = mBadUnderlineFamilyNames.ContainsSorted(key);
1156       bool classic =
1157           aForceClassicFams && aForceClassicFams->ContainsSorted(key);
1158       FontVisibility visibility;
1159       // Special case: hide the "Gill Sans" family that contains only UltraBold
1160       // faces, as this leads to breakage on sites with CSS that targeted the
1161       // Gill Sans family as found on macOS. (Bug 551313, bug 1632738)
1162       // TODO (jfkthame): the ultrabold faces from Gill Sans should be treated
1163       // as belonging to the Gill Sans MT family.
1164       if (key.EqualsLiteral("gill sans") && allFacesUltraBold(family)) {
1165         visibility = FontVisibility::Hidden;
1166       } else {
1167         visibility = aCollection == mSystemFonts ? GetVisibilityForFamily(name)
1168                                                  : FontVisibility::Base;
1169       }
1170       aFamilies.AppendElement(fontlist::Family::InitData(
1171           key, name, i, visibility, aCollection != mSystemFonts, bad, classic,
1172           altLocale));
1173     };
1174 
1175     unsigned count = localizedNames->GetCount();
1176     if (count == 1) {
1177       // This is the common case: the great majority of fonts only provide an
1178       // en-US family name.
1179       nsAutoCString name;
1180       if (!GetNameAsUtf8(name, localizedNames, 0)) {
1181         gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i;
1182         continue;
1183       }
1184       addFamily(name);
1185     } else {
1186       AutoTArray<nsCString, 4> names;
1187       int sysLocIndex = -1;
1188       for (unsigned index = 0; index < count; ++index) {
1189         nsAutoCString name;
1190         if (!GetNameAsUtf8(name, localizedNames, index)) {
1191           gfxWarning() << "GetNameAsUtf8 failed for index " << index
1192                        << " in font-family " << i;
1193           continue;
1194         }
1195         if (!names.Contains(name)) {
1196           if (sysLocIndex == -1) {
1197             WCHAR buf[32];
1198             if (SUCCEEDED(localizedNames->GetLocaleName(index, buf, 32))) {
1199               if (loc16.Equals(buf)) {
1200                 sysLocIndex = names.Length();
1201               }
1202             }
1203           }
1204           names.AppendElement(name);
1205         }
1206       }
1207       // If we didn't find a name that matched the system locale, use the
1208       // first (which is most often en-US).
1209       if (sysLocIndex == -1) {
1210         sysLocIndex = 0;
1211       }
1212       // Hack to work around EPSON fonts with bad names (tagged as MacRoman
1213       // but actually contain MacJapanese data): if we've chosen the first
1214       // name, *and* it is non-ASCII, *and* there is an alternative present,
1215       // use the next option instead as being more likely to be valid.
1216       if (sysLocIndex == 0 && names.Length() > 1 && !IsAscii(names[0])) {
1217         sysLocIndex = 1;
1218       }
1219       for (unsigned index = 0; index < names.Length(); ++index) {
1220         addFamily(names[index], index != sysLocIndex);
1221       }
1222     }
1223   }
1224 }
1225 
GetFacesInitDataForFamily(const fontlist::Family * aFamily,nsTArray<fontlist::Face::InitData> & aFaces,bool aLoadCmaps) const1226 void gfxDWriteFontList::GetFacesInitDataForFamily(
1227     const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces,
1228     bool aLoadCmaps) const {
1229   IDWriteFontCollection* collection =
1230 #ifdef MOZ_BUNDLED_FONTS
1231       aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1232 #else
1233       mSystemFonts;
1234 #endif
1235   if (!collection) {
1236     return;
1237   }
1238   RefPtr<IDWriteFontFamily> family;
1239   collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
1240   for (unsigned i = 0; i < family->GetFontCount(); ++i) {
1241     RefPtr<IDWriteFont> dwFont;
1242     family->GetFont(i, getter_AddRefs(dwFont));
1243     if (!dwFont || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
1244       continue;
1245     }
1246     DWRITE_FONT_STYLE dwstyle = dwFont->GetStyle();
1247     // Ignore italic styles of Meiryo because "Meiryo (Bold) Italic" has
1248     // non-italic style glyphs as Japanese characters.  However, using it
1249     // causes serious problem if web pages wants some elements to be
1250     // different style from others only with font-style.  For example,
1251     // <em> and <i> should be rendered as italic in the default style.
1252     if (dwstyle != DWRITE_FONT_STYLE_NORMAL &&
1253         aFamily->Key().AsString(SharedFontList()).EqualsLiteral("meiryo")) {
1254       continue;
1255     }
1256     WeightRange weight(FontWeight(dwFont->GetWeight()));
1257     StretchRange stretch(FontStretchFromDWriteStretch(dwFont->GetStretch()));
1258     // Try to read PSName as a unique face identifier; if this fails we'll get
1259     // it directly from the 'name' table, and if that also fails we consider
1260     // the face unusable.
1261     MOZ_SEH_TRY {
1262       nsAutoCString name;
1263       RefPtr<gfxCharacterMap> charmap;
1264       if (FAILED(GetDirectWriteFaceName(dwFont, PSNAME_ID, name)) ||
1265           aLoadCmaps) {
1266         RefPtr<IDWriteFontFace> dwFontFace;
1267         if (SUCCEEDED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1268           if (name.IsEmpty()) {
1269             GetPostScriptNameFromNameTable(dwFontFace, name);
1270           }
1271           const auto kCMAP =
1272               NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1273           const char* data;
1274           UINT32 size;
1275           void* context;
1276           BOOL exists;
1277           if (aLoadCmaps) {
1278             if (SUCCEEDED(dwFontFace->TryGetFontTable(
1279                     kCMAP, (const void**)&data, &size, &context, &exists)) &&
1280                 exists) {
1281               charmap = new gfxCharacterMap();
1282               uint32_t offset;
1283               gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap,
1284                                      offset);
1285               dwFontFace->ReleaseFontTable(context);
1286             }
1287           }
1288         }
1289       }
1290       if (name.IsEmpty()) {
1291         gfxWarning() << "Failed to get name for face " << i << " in family "
1292                      << aFamily->Key().AsString(SharedFontList()).get();
1293         continue;
1294       }
1295       SlantStyleRange slant(
1296           dwstyle == DWRITE_FONT_STYLE_NORMAL   ? FontSlantStyle::Normal()
1297           : dwstyle == DWRITE_FONT_STYLE_ITALIC ? FontSlantStyle::Italic()
1298                                                 : FontSlantStyle::Oblique());
1299       aFaces.AppendElement(fontlist::Face::InitData{
1300           name, uint16_t(i), false, weight, stretch, slant, charmap});
1301     }
1302     MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1303       // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
1304       // the font resource. We'll just skip the bad face.
1305       gfxCriticalNote << "Exception occurred reading faces for "
1306                       << aFamily->Key().AsString(SharedFontList()).get();
1307     }
1308   }
1309 }
1310 
ReadFaceNames(fontlist::Family * aFamily,fontlist::Face * aFace,nsCString & aPSName,nsCString & aFullName)1311 bool gfxDWriteFontList::ReadFaceNames(fontlist::Family* aFamily,
1312                                       fontlist::Face* aFace, nsCString& aPSName,
1313                                       nsCString& aFullName) {
1314   IDWriteFontCollection* collection =
1315 #ifdef MOZ_BUNDLED_FONTS
1316       aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1317 #else
1318       mSystemFonts;
1319 #endif
1320   RefPtr<IDWriteFontFamily> family;
1321   if (FAILED(collection->GetFontFamily(aFamily->Index(),
1322                                        getter_AddRefs(family)))) {
1323     MOZ_ASSERT_UNREACHABLE("failed to get font-family");
1324     return false;
1325   }
1326   RefPtr<IDWriteFont> dwFont;
1327   if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(dwFont)))) {
1328     MOZ_ASSERT_UNREACHABLE("failed to get font from family");
1329     return false;
1330   }
1331   HRESULT ps = GetDirectWriteFaceName(dwFont, PSNAME_ID, aPSName);
1332   HRESULT full = GetDirectWriteFaceName(dwFont, FULLNAME_ID, aFullName);
1333   if (FAILED(ps) || FAILED(full) || aPSName.IsEmpty() || aFullName.IsEmpty()) {
1334     // We'll return true if either name was found, false if both fail.
1335     // Note that on older Win7 systems, GetDirectWriteFaceName may "succeed"
1336     // but return an empty string, so we have to check for non-empty strings
1337     // to be sure we actually got a usable name.
1338 
1339     // Initialize result to true if either name was already found.
1340     bool result = (SUCCEEDED(ps) && !aPSName.IsEmpty()) ||
1341                   (SUCCEEDED(full) && !aFullName.IsEmpty());
1342     RefPtr<IDWriteFontFace> dwFontFace;
1343     if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1344       NS_WARNING("failed to create font face");
1345       return result;
1346     }
1347     void* context;
1348     const char* data;
1349     UINT32 size;
1350     BOOL exists;
1351     if (FAILED(dwFontFace->TryGetFontTable(
1352             NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1353             (const void**)&data, &size, &context, &exists)) ||
1354         !exists) {
1355       NS_WARNING("failed to get name table");
1356       return result;
1357     }
1358     MOZ_SEH_TRY {
1359       // Try to read the name table entries, and ensure result is true if either
1360       // one succeeds.
1361       if (FAILED(ps) || aPSName.IsEmpty()) {
1362         if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1363                 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aPSName))) {
1364           result = true;
1365         } else {
1366           NS_WARNING("failed to read psname");
1367         }
1368       }
1369       if (FAILED(full) || aFullName.IsEmpty()) {
1370         if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1371                 data, size, gfxFontUtils::NAME_ID_FULL, aFullName))) {
1372           result = true;
1373         } else {
1374           NS_WARNING("failed to read fullname");
1375         }
1376       }
1377     }
1378     MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1379       gfxCriticalNote << "Exception occurred reading face names for "
1380                       << aFamily->Key().AsString(SharedFontList()).get();
1381     }
1382     if (dwFontFace && context) {
1383       dwFontFace->ReleaseFontTable(context);
1384     }
1385     return result;
1386   }
1387   return true;
1388 }
1389 
ReadFaceNamesForFamily(fontlist::Family * aFamily,bool aNeedFullnamePostscriptNames)1390 void gfxDWriteFontList::ReadFaceNamesForFamily(
1391     fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) {
1392   if (!aFamily->IsInitialized()) {
1393     if (!InitializeFamily(aFamily)) {
1394       return;
1395     }
1396   }
1397   IDWriteFontCollection* collection =
1398 #ifdef MOZ_BUNDLED_FONTS
1399       aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1400 #else
1401       mSystemFonts;
1402 #endif
1403   RefPtr<IDWriteFontFamily> family;
1404   if (FAILED(collection->GetFontFamily(aFamily->Index(),
1405                                        getter_AddRefs(family)))) {
1406     return;
1407   }
1408   fontlist::FontList* list = SharedFontList();
1409   const fontlist::Pointer* facePtrs = aFamily->Faces(list);
1410   nsAutoCString familyName(aFamily->DisplayName().AsString(list));
1411   nsAutoCString key(aFamily->Key().AsString(list));
1412 
1413   // Read PS-names and fullnames of the faces, and any alternate family names
1414   // (either localizations or legacy subfamily names)
1415   for (unsigned i = 0; i < aFamily->NumFaces(); ++i) {
1416     auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
1417     if (!face) {
1418       continue;
1419     }
1420     RefPtr<IDWriteFont> dwFont;
1421     if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) {
1422       continue;
1423     }
1424     RefPtr<IDWriteFontFace> dwFontFace;
1425     if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1426       continue;
1427     }
1428 
1429     const char* data;
1430     UINT32 size;
1431     void* context;
1432     BOOL exists;
1433     if (FAILED(dwFontFace->TryGetFontTable(
1434             NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1435             (const void**)&data, &size, &context, &exists)) ||
1436         !exists) {
1437       continue;
1438     }
1439 
1440     MOZ_SEH_TRY {
1441       AutoTArray<nsCString, 4> otherFamilyNames;
1442       gfxFontUtils::ReadOtherFamilyNamesForFace(familyName, data, size,
1443                                                 otherFamilyNames, false);
1444       for (const auto& alias : otherFamilyNames) {
1445         nsAutoCString key(alias);
1446         ToLowerCase(key);
1447         auto aliasData = mAliasTable.GetOrInsertNew(key);
1448         aliasData->InitFromFamily(aFamily, familyName);
1449         aliasData->mFaces.AppendElement(facePtrs[i]);
1450       }
1451 
1452       nsAutoCString psname, fullname;
1453       if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1454               data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, psname))) {
1455         ToLowerCase(psname);
1456         mLocalNameTable.InsertOrUpdate(
1457             psname, fontlist::LocalFaceRec::InitData(key, i));
1458       }
1459       if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1460               data, size, gfxFontUtils::NAME_ID_FULL, fullname))) {
1461         ToLowerCase(fullname);
1462         if (fullname != psname) {
1463           mLocalNameTable.InsertOrUpdate(
1464               fullname, fontlist::LocalFaceRec::InitData(key, i));
1465         }
1466       }
1467     }
1468     MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1469       gfxCriticalNote << "Exception occurred reading names for "
1470                       << familyName.get();
1471     }
1472 
1473     dwFontFace->ReleaseFontTable(context);
1474   }
1475 }
1476 
1477 enum DWriteInitError {
1478   errGDIInterop = 1,
1479   errSystemFontCollection = 2,
1480   errNoFonts = 3
1481 };
1482 
InitSharedFontListForPlatform()1483 void gfxDWriteFontList::InitSharedFontListForPlatform() {
1484   mGDIFontTableAccess = Preferences::GetBool(
1485       "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
1486   mForceGDIClassicMaxFontSize = Preferences::GetInt(
1487       "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
1488       mForceGDIClassicMaxFontSize);
1489 
1490   mSubstitutions.Clear();
1491   mNonExistingFonts.Clear();
1492 
1493   RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
1494   HRESULT hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
1495   if (FAILED(hr)) {
1496     Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1497                           uint32_t(errGDIInterop));
1498     mSharedFontList.reset(nullptr);
1499     return;
1500   }
1501 
1502   mSystemFonts = Factory::GetDWriteSystemFonts(true);
1503   NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
1504   if (!mSystemFonts) {
1505     Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1506                           uint32_t(errSystemFontCollection));
1507     mSharedFontList.reset(nullptr);
1508     return;
1509   }
1510 #ifdef MOZ_BUNDLED_FONTS
1511   // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1512   // explicit value of 0 (off) will disable them.
1513   TimeStamp start1 = TimeStamp::Now();
1514   if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1515     mBundledFonts = CreateBundledFontsCollection(factory);
1516   }
1517   TimeStamp end1 = TimeStamp::Now();
1518 #endif
1519 
1520   if (XRE_IsParentProcess()) {
1521     nsAutoCString classicFamilies;
1522     AutoTArray<nsCString, 16> forceClassicFams;
1523     nsresult rv = Preferences::GetCString(
1524         "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
1525         classicFamilies);
1526     if (NS_SUCCEEDED(rv)) {
1527       for (auto name :
1528            nsCCharSeparatedTokenizer(classicFamilies, ',').ToRange()) {
1529         BuildKeyNameFromFontName(name);
1530         forceClassicFams.AppendElement(name);
1531       }
1532       forceClassicFams.Sort();
1533     }
1534     nsTArray<fontlist::Family::InitData> families;
1535     AppendFamiliesFromCollection(mSystemFonts, families, &forceClassicFams);
1536 #ifdef MOZ_BUNDLED_FONTS
1537     if (mBundledFonts) {
1538       TimeStamp start2 = TimeStamp::Now();
1539       AppendFamiliesFromCollection(mBundledFonts, families);
1540       TimeStamp end2 = TimeStamp::Now();
1541       Telemetry::Accumulate(
1542           Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1543           (end1 - start1).ToMilliseconds() + (end2 - start2).ToMilliseconds());
1544     }
1545 #endif
1546     SharedFontList()->SetFamilyNames(families);
1547     GetPrefsAndStartLoader();
1548   }
1549 
1550   if (!SharedFontList()->Initialized()) {
1551     return;
1552   }
1553 
1554   GetDirectWriteSubstitutes();
1555   GetFontSubstitutes();
1556 }
1557 
InitFontListForPlatform()1558 nsresult gfxDWriteFontList::InitFontListForPlatform() {
1559   LARGE_INTEGER frequency;           // ticks per second
1560   LARGE_INTEGER t1, t2, t3, t4, t5;  // ticks
1561   double elapsedTime, upTime;
1562   char nowTime[256], nowDate[256];
1563 
1564   if (LOG_FONTINIT_ENABLED()) {
1565     GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, nullptr, nullptr,
1566                    nowTime, 256);
1567     GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
1568     upTime = (double)GetTickCount();
1569   }
1570   QueryPerformanceFrequency(&frequency);
1571   QueryPerformanceCounter(&t1);  // start
1572 
1573   HRESULT hr;
1574   mGDIFontTableAccess = Preferences::GetBool(
1575       "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
1576 
1577   mFontSubstitutes.Clear();
1578   mNonExistingFonts.Clear();
1579 
1580   RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
1581 
1582   hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
1583   if (FAILED(hr)) {
1584     Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1585                           uint32_t(errGDIInterop));
1586     return NS_ERROR_FAILURE;
1587   }
1588 
1589   QueryPerformanceCounter(&t2);  // base-class/interop initialization
1590 
1591   mSystemFonts = Factory::GetDWriteSystemFonts(true);
1592   NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
1593 
1594   if (!mSystemFonts) {
1595     Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1596                           uint32_t(errSystemFontCollection));
1597     return NS_ERROR_FAILURE;
1598   }
1599 
1600 #ifdef MOZ_BUNDLED_FONTS
1601   // Get bundled fonts before the system collection, so that in the case of
1602   // duplicate names, we have recorded the family as bundled (and therefore
1603   // available regardless of visibility settings).
1604   // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1605   // explicit value of 0 (off) will disable them.
1606   if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1607     TimeStamp start = TimeStamp::Now();
1608     mBundledFonts = CreateBundledFontsCollection(factory);
1609     if (mBundledFonts) {
1610       GetFontsFromCollection(mBundledFonts);
1611     }
1612     TimeStamp end = TimeStamp::Now();
1613     Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1614                           (end - start).ToMilliseconds());
1615   }
1616 #endif
1617   const uint32_t kBundledCount = mFontFamilies.Count();
1618 
1619   QueryPerformanceCounter(&t3);  // system font collection
1620 
1621   GetFontsFromCollection(mSystemFonts);
1622 
1623   // if no fonts found, something is out of whack, bail and use GDI backend
1624   NS_ASSERTION(mFontFamilies.Count() > kBundledCount,
1625                "no fonts found in the system fontlist -- holy crap batman!");
1626   if (mFontFamilies.Count() == kBundledCount) {
1627     Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1628                           uint32_t(errNoFonts));
1629     return NS_ERROR_FAILURE;
1630   }
1631 
1632   QueryPerformanceCounter(&t4);  // iterate over system fonts
1633 
1634   mOtherFamilyNamesInitialized = true;
1635   GetFontSubstitutes();
1636 
1637   // bug 642093 - DirectWrite does not support old bitmap (.fon)
1638   // font files, but a few of these such as "Courier" and "MS Sans Serif"
1639   // are frequently specified in shoddy CSS, without appropriate fallbacks.
1640   // By mapping these to TrueType equivalents, we provide better consistency
1641   // with both pre-DW systems and with IE9, which appears to do the same.
1642   GetDirectWriteSubstitutes();
1643 
1644   // bug 551313 - DirectWrite creates a Gill Sans family out of
1645   // poorly named members of the Gill Sans MT family containing
1646   // only Ultra Bold weights.  This causes big problems for pages
1647   // using Gill Sans which is usually only available on OSX
1648 
1649   nsAutoCString nameGillSans("Gill Sans");
1650   nsAutoCString nameGillSansMT("Gill Sans MT");
1651   BuildKeyNameFromFontName(nameGillSans);
1652   BuildKeyNameFromFontName(nameGillSansMT);
1653 
1654   gfxFontFamily* gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
1655   gfxFontFamily* gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
1656 
1657   if (gillSansFamily && gillSansMTFamily) {
1658     gillSansFamily->FindStyleVariations();
1659     nsTArray<RefPtr<gfxFontEntry> >& faces = gillSansFamily->GetFontList();
1660     uint32_t i;
1661 
1662     bool allUltraBold = true;
1663     for (i = 0; i < faces.Length(); i++) {
1664       // does the face have 'Ultra Bold' in the name?
1665       if (faces[i]->Name().Find("Ultra Bold"_ns) == -1) {
1666         allUltraBold = false;
1667         break;
1668       }
1669     }
1670 
1671     // if all the Gill Sans faces are Ultra Bold ==> move faces
1672     // for Gill Sans into Gill Sans MT family
1673     if (allUltraBold) {
1674       // add faces to Gill Sans MT
1675       for (i = 0; i < faces.Length(); i++) {
1676         // change the entry's family name to match its adoptive family
1677         faces[i]->mFamilyName = gillSansMTFamily->Name();
1678         gillSansMTFamily->AddFontEntry(faces[i]);
1679 
1680         if (LOG_FONTLIST_ENABLED()) {
1681           gfxFontEntry* fe = faces[i];
1682           nsAutoCString weightString;
1683           fe->Weight().ToString(weightString);
1684           LOG_FONTLIST(
1685               ("(fontlist) moved (%s) to family (%s)"
1686                " with style: %s weight: %s stretch: %d",
1687                fe->Name().get(), gillSansMTFamily->Name().get(),
1688                (fe->IsItalic()) ? "italic"
1689                                 : (fe->IsOblique() ? "oblique" : "normal"),
1690                weightString.get(), fe->Stretch()));
1691         }
1692       }
1693 
1694       // remove Gills Sans
1695       mFontFamilies.Remove(nameGillSans);
1696     }
1697   }
1698 
1699   nsAutoCString classicFamilies;
1700   nsresult rv = Preferences::GetCString(
1701       "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
1702       classicFamilies);
1703   if (NS_SUCCEEDED(rv)) {
1704     nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
1705     while (tokenizer.hasMoreTokens()) {
1706       nsAutoCString name(tokenizer.nextToken());
1707       BuildKeyNameFromFontName(name);
1708       gfxFontFamily* family = mFontFamilies.GetWeak(name);
1709       if (family) {
1710         static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true);
1711       }
1712     }
1713   }
1714   mForceGDIClassicMaxFontSize = Preferences::GetInt(
1715       "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
1716       mForceGDIClassicMaxFontSize);
1717 
1718   GetPrefsAndStartLoader();
1719 
1720   QueryPerformanceCounter(&t5);  // misc initialization
1721 
1722   if (LOG_FONTINIT_ENABLED()) {
1723     // determine dwrite version
1724     nsAutoString dwriteVers;
1725     gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
1726     LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime));
1727     LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime / 1000));
1728     LOG_FONTINIT(("(fontinit) dwrite version: %s\n",
1729                   NS_ConvertUTF16toUTF8(dwriteVers).get()));
1730   }
1731 
1732   elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
1733   Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_TOTAL,
1734                         elapsedTime);
1735   Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT,
1736                         mSystemFonts->GetFontFamilyCount());
1737   LOG_FONTINIT((
1738       "(fontinit) Total time in InitFontList:    %9.3f ms (families: %d, %s)\n",
1739       elapsedTime, mSystemFonts->GetFontFamilyCount(),
1740       (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
1741 
1742   elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
1743   LOG_FONTINIT(
1744       ("(fontinit)  --- base/interop obj initialization init: %9.3f ms\n",
1745        elapsedTime));
1746 
1747   elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
1748   Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT,
1749                         elapsedTime);
1750   LOG_FONTINIT(
1751       ("(fontinit)  --- GetSystemFontCollection:  %9.3f ms\n", elapsedTime));
1752 
1753   elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart;
1754   LOG_FONTINIT(
1755       ("(fontinit)  --- iterate over families:    %9.3f ms\n", elapsedTime));
1756 
1757   elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart;
1758   LOG_FONTINIT(
1759       ("(fontinit)  --- misc initialization:    %9.3f ms\n", elapsedTime));
1760 
1761   return NS_OK;
1762 }
1763 
GetFontsFromCollection(IDWriteFontCollection * aCollection)1764 void gfxDWriteFontList::GetFontsFromCollection(
1765     IDWriteFontCollection* aCollection) {
1766   for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) {
1767     RefPtr<IDWriteFontFamily> family;
1768     aCollection->GetFontFamily(i, getter_AddRefs(family));
1769 
1770     RefPtr<IDWriteLocalizedStrings> names;
1771     HRESULT hr = family->GetFamilyNames(getter_AddRefs(names));
1772     if (FAILED(hr)) {
1773       continue;
1774     }
1775 
1776     nsAutoCString name;
1777     if (!GetEnglishOrFirstName(name, names)) {
1778       continue;
1779     }
1780     nsAutoCString familyName(
1781         name);  // keep a copy before we lowercase it as a key
1782 
1783     BuildKeyNameFromFontName(name);
1784 
1785     RefPtr<gfxFontFamily> fam;
1786 
1787     if (mFontFamilies.GetWeak(name)) {
1788       continue;
1789     }
1790 
1791     FontVisibility visibility = aCollection == mSystemFonts
1792                                     ? GetVisibilityForFamily(familyName)
1793                                     : FontVisibility::Base;
1794 
1795     fam = new gfxDWriteFontFamily(familyName, visibility, family,
1796                                   aCollection == mSystemFonts);
1797     if (!fam) {
1798       continue;
1799     }
1800 
1801     if (mBadUnderlineFamilyNames.ContainsSorted(name)) {
1802       fam->SetBadUnderlineFamily();
1803     }
1804     mFontFamilies.InsertOrUpdate(name, RefPtr{fam});
1805 
1806     // now add other family name localizations, if present
1807     uint32_t nameCount = names->GetCount();
1808     uint32_t nameIndex;
1809 
1810     if (nameCount > 1) {
1811       UINT32 englishIdx = 0;
1812       BOOL exists;
1813       // if this fails/doesn't exist, we'll have used name index 0,
1814       // so that's the one we'll want to skip here
1815       names->FindLocaleName(L"en-us", &englishIdx, &exists);
1816 
1817       for (nameIndex = 0; nameIndex < nameCount; nameIndex++) {
1818         UINT32 nameLen;
1819         AutoTArray<WCHAR, 32> localizedName;
1820 
1821         // only add other names
1822         if (nameIndex == englishIdx) {
1823           continue;
1824         }
1825 
1826         hr = names->GetStringLength(nameIndex, &nameLen);
1827         if (FAILED(hr)) {
1828           continue;
1829         }
1830 
1831         if (!localizedName.SetLength(nameLen + 1, fallible)) {
1832           continue;
1833         }
1834 
1835         hr = names->GetString(nameIndex, localizedName.Elements(), nameLen + 1);
1836         if (FAILED(hr)) {
1837           continue;
1838         }
1839 
1840         NS_ConvertUTF16toUTF8 locName(localizedName.Elements());
1841 
1842         if (!familyName.Equals(locName)) {
1843           AddOtherFamilyName(fam, locName);
1844         }
1845       }
1846     }
1847 
1848     // at this point, all family names have been read in
1849     fam->SetOtherFamilyNamesInitialized();
1850   }
1851 }
1852 
RemoveCharsetFromFontSubstitute(nsACString & aName)1853 static void RemoveCharsetFromFontSubstitute(nsACString& aName) {
1854   int32_t comma = aName.FindChar(',');
1855   if (comma >= 0) aName.Truncate(comma);
1856 }
1857 
1858 #define MAX_VALUE_NAME 512
1859 #define MAX_VALUE_DATA 512
1860 
GetFontSubstitutes()1861 nsresult gfxDWriteFontList::GetFontSubstitutes() {
1862   HKEY hKey;
1863   DWORD i, rv, lenAlias, lenActual, valueType;
1864   WCHAR aliasName[MAX_VALUE_NAME];
1865   WCHAR actualName[MAX_VALUE_DATA];
1866 
1867   if (RegOpenKeyExW(
1868           HKEY_LOCAL_MACHINE,
1869           L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
1870           0, KEY_READ, &hKey) != ERROR_SUCCESS) {
1871     return NS_ERROR_FAILURE;
1872   }
1873 
1874   for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
1875     aliasName[0] = 0;
1876     lenAlias = ArrayLength(aliasName);
1877     actualName[0] = 0;
1878     lenActual = sizeof(actualName);
1879     rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
1880                        (LPBYTE)actualName, &lenActual);
1881 
1882     if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
1883       continue;
1884     }
1885 
1886     if (aliasName[0] == WCHAR('@')) {
1887       continue;
1888     }
1889 
1890     NS_ConvertUTF16toUTF8 substituteName((char16_t*)aliasName);
1891     NS_ConvertUTF16toUTF8 actualFontName((char16_t*)actualName);
1892     RemoveCharsetFromFontSubstitute(substituteName);
1893     BuildKeyNameFromFontName(substituteName);
1894     RemoveCharsetFromFontSubstitute(actualFontName);
1895     BuildKeyNameFromFontName(actualFontName);
1896     if (SharedFontList()) {
1897       // Skip substitution if the original font is available, unless the option
1898       // to apply substitutions unconditionally is enabled.
1899       if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
1900         // Font substitutions are recorded for the canonical family names; we
1901         // don't need FindFamily to consider localized aliases when searching.
1902         if (SharedFontList()->FindFamily(substituteName,
1903                                          /*aPrimaryNameOnly*/ true)) {
1904           continue;
1905         }
1906       }
1907       if (SharedFontList()->FindFamily(actualFontName,
1908                                        /*aPrimaryNameOnly*/ true)) {
1909         mSubstitutions.InsertOrUpdate(substituteName,
1910                                       MakeUnique<nsCString>(actualFontName));
1911       } else if (mSubstitutions.Get(actualFontName)) {
1912         mSubstitutions.InsertOrUpdate(
1913             substituteName,
1914             MakeUnique<nsCString>(*mSubstitutions.Get(actualFontName)));
1915       } else {
1916         mNonExistingFonts.AppendElement(substituteName);
1917       }
1918     } else {
1919       gfxFontFamily* ff;
1920       if (!actualFontName.IsEmpty() &&
1921           (ff = mFontFamilies.GetWeak(actualFontName))) {
1922         mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
1923       } else {
1924         mNonExistingFonts.AppendElement(substituteName);
1925       }
1926     }
1927   }
1928   return NS_OK;
1929 }
1930 
1931 struct FontSubstitution {
1932   const char* aliasName;
1933   const char* actualName;
1934 };
1935 
1936 static const FontSubstitution sDirectWriteSubs[] = {
1937     {"MS Sans Serif", "Microsoft Sans Serif"},
1938     {"MS Serif", "Times New Roman"},
1939     {"Courier", "Courier New"},
1940     {"Small Fonts", "Arial"},
1941     {"Roman", "Times New Roman"},
1942     {"Script", "Mistral"}};
1943 
GetDirectWriteSubstitutes()1944 void gfxDWriteFontList::GetDirectWriteSubstitutes() {
1945   for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) {
1946     const FontSubstitution& sub(sDirectWriteSubs[i]);
1947     nsAutoCString substituteName(sub.aliasName);
1948     BuildKeyNameFromFontName(substituteName);
1949     if (SharedFontList()) {
1950       // Skip substitution if the original font is available, unless the option
1951       // to apply substitutions unconditionally is enabled.
1952       if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
1953         // We don't need FindFamily to consider localized aliases when searching
1954         // for the DirectWrite substitutes, we know the canonical names.
1955         if (SharedFontList()->FindFamily(substituteName,
1956                                          /*aPrimaryNameOnly*/ true)) {
1957           continue;
1958         }
1959       }
1960       nsAutoCString actualFontName(sub.actualName);
1961       BuildKeyNameFromFontName(actualFontName);
1962       if (SharedFontList()->FindFamily(actualFontName,
1963                                        /*aPrimaryNameOnly*/ true)) {
1964         mSubstitutions.InsertOrUpdate(substituteName,
1965                                       MakeUnique<nsCString>(actualFontName));
1966       } else {
1967         mNonExistingFonts.AppendElement(substituteName);
1968       }
1969     } else {
1970       if (nullptr != mFontFamilies.GetWeak(substituteName)) {
1971         // don't do the substitution if user actually has a usable font
1972         // with this name installed
1973         continue;
1974       }
1975       nsAutoCString actualFontName(sub.actualName);
1976       BuildKeyNameFromFontName(actualFontName);
1977       gfxFontFamily* ff;
1978       if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
1979         mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
1980       } else {
1981         mNonExistingFonts.AppendElement(substituteName);
1982       }
1983     }
1984   }
1985 }
1986 
FindAndAddFamilies(StyleGenericFontFamily aGeneric,const nsACString & aFamily,nsTArray<FamilyAndGeneric> * aOutput,FindFamiliesFlags aFlags,gfxFontStyle * aStyle,nsAtom * aLanguage,gfxFloat aDevToCssSize)1987 bool gfxDWriteFontList::FindAndAddFamilies(
1988     StyleGenericFontFamily aGeneric, const nsACString& aFamily,
1989     nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
1990     gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
1991   nsAutoCString keyName(aFamily);
1992   BuildKeyNameFromFontName(keyName);
1993 
1994   if (SharedFontList()) {
1995     nsACString* subst = mSubstitutions.Get(keyName);
1996     if (subst) {
1997       keyName = *subst;
1998     }
1999   } else {
2000     gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
2001     if (ff) {
2002       aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
2003       return true;
2004     }
2005   }
2006 
2007   if (mNonExistingFonts.Contains(keyName)) {
2008     return false;
2009   }
2010 
2011   return gfxPlatformFontList::FindAndAddFamilies(
2012       aGeneric, keyName, aOutput, aFlags, aStyle, aLanguage, aDevToCssSize);
2013 }
2014 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const2015 void gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2016                                                FontListSizes* aSizes) const {
2017   gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2018 
2019   // We are a singleton, so include the font loader singleton's memory.
2020   MOZ_ASSERT(static_cast<const gfxPlatformFontList*>(this) ==
2021              gfxPlatformFontList::PlatformFontList());
2022   gfxDWriteFontFileLoader* loader = static_cast<gfxDWriteFontFileLoader*>(
2023       gfxDWriteFontFileLoader::Instance());
2024   aSizes->mLoaderSize += loader->SizeOfIncludingThis(aMallocSizeOf);
2025 
2026   aSizes->mFontListSize +=
2027       SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
2028 
2029   aSizes->mFontListSize +=
2030       mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
2031   for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
2032     aSizes->mFontListSize +=
2033         mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2034   }
2035 }
2036 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const2037 void gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2038                                                FontListSizes* aSizes) const {
2039   aSizes->mFontListSize += aMallocSizeOf(this);
2040   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2041 }
2042 
GetFamilyName(IDWriteFont * aFont,nsCString & aFamilyName)2043 static HRESULT GetFamilyName(IDWriteFont* aFont, nsCString& aFamilyName) {
2044   HRESULT hr;
2045   RefPtr<IDWriteFontFamily> family;
2046 
2047   // clean out previous value
2048   aFamilyName.Truncate();
2049 
2050   hr = aFont->GetFontFamily(getter_AddRefs(family));
2051   if (FAILED(hr)) {
2052     return hr;
2053   }
2054 
2055   RefPtr<IDWriteLocalizedStrings> familyNames;
2056 
2057   hr = family->GetFamilyNames(getter_AddRefs(familyNames));
2058   if (FAILED(hr)) {
2059     return hr;
2060   }
2061 
2062   if (!GetEnglishOrFirstName(aFamilyName, familyNames)) {
2063     return E_FAIL;
2064   }
2065 
2066   return S_OK;
2067 }
2068 
2069 // bug 705594 - the method below doesn't actually do any "drawing", it's only
2070 // used to invoke the DirectWrite layout engine to determine the fallback font
2071 // for a given character.
2072 
DrawGlyphRun(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_MEASURING_MODE measuringMode,DWRITE_GLYPH_RUN const * glyphRun,DWRITE_GLYPH_RUN_DESCRIPTION const * glyphRunDescription,IUnknown * clientDrawingEffect)2073 IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun(
2074     void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY,
2075     DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun,
2076     DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
2077     IUnknown* clientDrawingEffect) {
2078   if (!mSystemFonts) {
2079     return E_FAIL;
2080   }
2081 
2082   HRESULT hr = S_OK;
2083 
2084   RefPtr<IDWriteFont> font;
2085   hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
2086                                          getter_AddRefs(font));
2087   if (FAILED(hr)) {
2088     return hr;
2089   }
2090 
2091   // copy the family name
2092   hr = GetFamilyName(font, mFamilyName);
2093   if (FAILED(hr)) {
2094     return hr;
2095   }
2096 
2097   // Arial is used as the default fallback font
2098   // so if it matches ==> no font found
2099   if (mFamilyName.EqualsLiteral("Arial")) {
2100     mFamilyName.Truncate();
2101     return E_FAIL;
2102   }
2103   return hr;
2104 }
2105 
PlatformGlobalFontFallback(const uint32_t aCh,Script aRunScript,const gfxFontStyle * aMatchStyle,FontFamily & aMatchedFamily)2106 gfxFontEntry* gfxDWriteFontList::PlatformGlobalFontFallback(
2107     const uint32_t aCh, Script aRunScript, const gfxFontStyle* aMatchStyle,
2108     FontFamily& aMatchedFamily) {
2109   HRESULT hr;
2110 
2111   RefPtr<IDWriteFactory> dwFactory = Factory::GetDWriteFactory();
2112   if (!dwFactory) {
2113     return nullptr;
2114   }
2115 
2116   // initialize fallback renderer
2117   if (!mFallbackRenderer) {
2118     mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory);
2119   }
2120   if (!mFallbackRenderer->IsValid()) {
2121     return nullptr;
2122   }
2123 
2124   // initialize text format
2125   if (!mFallbackFormat) {
2126     hr = dwFactory->CreateTextFormat(
2127         L"Arial", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL,
2128         DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us",
2129         getter_AddRefs(mFallbackFormat));
2130     if (FAILED(hr)) {
2131       return nullptr;
2132     }
2133   }
2134 
2135   // set up string with fallback character
2136   wchar_t str[16];
2137   uint32_t strLen;
2138 
2139   if (IS_IN_BMP(aCh)) {
2140     str[0] = static_cast<wchar_t>(aCh);
2141     str[1] = 0;
2142     strLen = 1;
2143   } else {
2144     str[0] = static_cast<wchar_t>(H_SURROGATE(aCh));
2145     str[1] = static_cast<wchar_t>(L_SURROGATE(aCh));
2146     str[2] = 0;
2147     strLen = 2;
2148   }
2149 
2150   // set up layout
2151   RefPtr<IDWriteTextLayout> fallbackLayout;
2152 
2153   hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, 200.0f, 200.0f,
2154                                    getter_AddRefs(fallbackLayout));
2155   if (FAILED(hr)) {
2156     return nullptr;
2157   }
2158 
2159   // call the draw method to invoke the DirectWrite layout functions
2160   // which determine the fallback font
2161   MOZ_SEH_TRY {
2162     hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
2163     if (FAILED(hr)) {
2164       return nullptr;
2165     }
2166   }
2167   MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2168     gfxCriticalNote << "Exception occurred during DWrite font fallback";
2169     return nullptr;
2170   }
2171 
2172   FontFamily family = FindFamily(mFallbackRenderer->FallbackFamilyName());
2173   if (!family.IsNull()) {
2174     gfxFontEntry* fontEntry = nullptr;
2175     if (family.mIsShared) {
2176       auto face =
2177           family.mShared->FindFaceForStyle(SharedFontList(), *aMatchStyle);
2178       if (face) {
2179         fontEntry = GetOrCreateFontEntry(face, family.mShared);
2180       }
2181     } else {
2182       fontEntry = family.mUnshared->FindFontForStyle(*aMatchStyle);
2183     }
2184     if (fontEntry && fontEntry->HasCharacter(aCh)) {
2185       aMatchedFamily = family;
2186       return fontEntry;
2187     }
2188     Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true);
2189   }
2190 
2191   return nullptr;
2192 }
2193 
2194 // used to load system-wide font info on off-main thread
2195 class DirectWriteFontInfo : public FontInfoData {
2196  public:
DirectWriteFontInfo(bool aLoadOtherNames,bool aLoadFaceNames,bool aLoadCmaps,IDWriteFontCollection * aSystemFonts,IDWriteFontCollection * aBundledFonts)2197   DirectWriteFontInfo(bool aLoadOtherNames, bool aLoadFaceNames,
2198                       bool aLoadCmaps, IDWriteFontCollection* aSystemFonts
2199 #ifdef MOZ_BUNDLED_FONTS
2200                       ,
2201                       IDWriteFontCollection* aBundledFonts
2202 #endif
2203                       )
2204       : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps),
2205         mSystemFonts(aSystemFonts)
2206 #ifdef MOZ_BUNDLED_FONTS
2207         ,
2208         mBundledFonts(aBundledFonts)
2209 #endif
2210   {
2211   }
2212 
2213   virtual ~DirectWriteFontInfo() = default;
2214 
2215   // loads font data for all members of a given family
2216   virtual void LoadFontFamilyData(const nsACString& aFamilyName);
2217 
2218  private:
2219   RefPtr<IDWriteFontCollection> mSystemFonts;
2220 #ifdef MOZ_BUNDLED_FONTS
2221   RefPtr<IDWriteFontCollection> mBundledFonts;
2222 #endif
2223 };
2224 
LoadFontFamilyData(const nsACString & aFamilyName)2225 void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
2226   // lookup the family
2227   NS_ConvertUTF8toUTF16 famName(aFamilyName);
2228 
2229   HRESULT hr;
2230   BOOL exists = false;
2231 
2232   uint32_t index;
2233   RefPtr<IDWriteFontFamily> family;
2234   hr = mSystemFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
2235                                     &exists);
2236   if (SUCCEEDED(hr) && exists) {
2237     mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
2238     if (!family) {
2239       return;
2240     }
2241   }
2242 
2243 #ifdef MOZ_BUNDLED_FONTS
2244   if (!family && mBundledFonts) {
2245     hr = mBundledFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
2246                                        &exists);
2247     if (SUCCEEDED(hr) && exists) {
2248       mBundledFonts->GetFontFamily(index, getter_AddRefs(family));
2249     }
2250   }
2251 #endif
2252 
2253   if (!family) {
2254     return;
2255   }
2256 
2257   // later versions of DirectWrite support querying the fullname/psname
2258   bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
2259 
2260   for (uint32_t i = 0; i < family->GetFontCount(); i++) {
2261     // get the font
2262     RefPtr<IDWriteFont> dwFont;
2263     hr = family->GetFont(i, getter_AddRefs(dwFont));
2264     if (FAILED(hr)) {
2265       // This should never happen.
2266       NS_WARNING("Failed to get existing font from family.");
2267       continue;
2268     }
2269 
2270     if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
2271       // We don't want these in the font list; we'll apply simulations
2272       // on the fly when appropriate.
2273       continue;
2274     }
2275 
2276     mLoadStats.fonts++;
2277 
2278     // get the name of the face
2279     nsCString fullID(aFamilyName);
2280     nsAutoCString fontName;
2281     hr = GetDirectWriteFontName(dwFont, fontName);
2282     if (FAILED(hr)) {
2283       continue;
2284     }
2285     fullID.Append(' ');
2286     fullID.Append(fontName);
2287 
2288     FontFaceData fontData;
2289     bool haveData = true;
2290     RefPtr<IDWriteFontFace> dwFontFace;
2291 
2292     if (mLoadFaceNames) {
2293       // try to load using DirectWrite first
2294       if (loadFaceNamesUsingDirectWrite) {
2295         hr =
2296             GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
2297         if (FAILED(hr)) {
2298           loadFaceNamesUsingDirectWrite = false;
2299         }
2300         hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
2301         if (FAILED(hr)) {
2302           loadFaceNamesUsingDirectWrite = false;
2303         }
2304       }
2305 
2306       // if DirectWrite read fails, load directly from name table
2307       if (!loadFaceNamesUsingDirectWrite) {
2308         hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
2309         if (SUCCEEDED(hr)) {
2310           uint32_t kNAME =
2311               NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
2312           const char* nameData;
2313           BOOL exists;
2314           void* ctx;
2315           uint32_t nameSize;
2316 
2317           hr = dwFontFace->TryGetFontTable(kNAME, (const void**)&nameData,
2318                                            &nameSize, &ctx, &exists);
2319           if (SUCCEEDED(hr) && nameData && nameSize > 0) {
2320             MOZ_SEH_TRY {
2321               gfxFontUtils::ReadCanonicalName(nameData, nameSize,
2322                                               gfxFontUtils::NAME_ID_FULL,
2323                                               fontData.mFullName);
2324               gfxFontUtils::ReadCanonicalName(nameData, nameSize,
2325                                               gfxFontUtils::NAME_ID_POSTSCRIPT,
2326                                               fontData.mPostscriptName);
2327             }
2328             MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2329               gfxCriticalNote << "Exception occurred reading names for "
2330                               << PromiseFlatCString(aFamilyName).get();
2331             }
2332             dwFontFace->ReleaseFontTable(ctx);
2333           }
2334         }
2335       }
2336 
2337       haveData =
2338           !fontData.mPostscriptName.IsEmpty() || !fontData.mFullName.IsEmpty();
2339       if (haveData) {
2340         mLoadStats.facenames++;
2341       }
2342     }
2343 
2344     // cmaps
2345     if (mLoadCmaps) {
2346       if (!dwFontFace) {
2347         hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
2348         if (!SUCCEEDED(hr)) {
2349           continue;
2350         }
2351       }
2352 
2353       uint32_t kCMAP =
2354           NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
2355       const uint8_t* cmapData;
2356       BOOL exists;
2357       void* ctx;
2358       uint32_t cmapSize;
2359 
2360       hr = dwFontFace->TryGetFontTable(kCMAP, (const void**)&cmapData,
2361                                        &cmapSize, &ctx, &exists);
2362 
2363       if (SUCCEEDED(hr) && exists) {
2364         bool cmapLoaded = false;
2365         RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
2366         uint32_t offset;
2367         MOZ_SEH_TRY {
2368           if (cmapData && cmapSize > 0 &&
2369               NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
2370                                                   offset))) {
2371             fontData.mCharacterMap = charmap;
2372             fontData.mUVSOffset = offset;
2373             cmapLoaded = true;
2374             mLoadStats.cmaps++;
2375           }
2376         }
2377         MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2378           gfxCriticalNote << "Exception occurred reading cmaps for "
2379                           << PromiseFlatCString(aFamilyName).get();
2380         }
2381         dwFontFace->ReleaseFontTable(ctx);
2382         haveData = haveData || cmapLoaded;
2383       }
2384     }
2385 
2386     // if have data, load
2387     if (haveData) {
2388       mFontFaceData.InsertOrUpdate(fullID, fontData);
2389     }
2390   }
2391 }
2392 
CreateFontInfoData()2393 already_AddRefed<FontInfoData> gfxDWriteFontList::CreateFontInfoData() {
2394   bool loadCmaps = !UsesSystemFallback() ||
2395                    gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
2396 
2397   RefPtr<DirectWriteFontInfo> fi = new DirectWriteFontInfo(
2398       false, NeedFullnamePostscriptNames(), loadCmaps, mSystemFonts
2399 #ifdef MOZ_BUNDLED_FONTS
2400       ,
2401       mBundledFonts
2402 #endif
2403   );
2404 
2405   return fi.forget();
2406 }
2407 
CreateFontFamily(const nsACString & aName,FontVisibility aVisibility) const2408 gfxFontFamily* gfxDWriteFontList::CreateFontFamily(
2409     const nsACString& aName, FontVisibility aVisibility) const {
2410   return new gfxDWriteFontFamily(aName, aVisibility, nullptr);
2411 }
2412 
2413 #ifdef MOZ_BUNDLED_FONTS
2414 
2415 #  define IMPL_QI_FOR_DWRITE(_interface)                             \
2416    public:                                                           \
2417     IFACEMETHOD(QueryInterface)(IID const& riid, void** ppvObject) { \
2418       if (__uuidof(_interface) == riid) {                            \
2419         *ppvObject = this;                                           \
2420       } else if (__uuidof(IUnknown) == riid) {                       \
2421         *ppvObject = this;                                           \
2422       } else {                                                       \
2423         *ppvObject = nullptr;                                        \
2424         return E_NOINTERFACE;                                        \
2425       }                                                              \
2426       this->AddRef();                                                \
2427       return S_OK;                                                   \
2428     }
2429 
2430 class BundledFontFileEnumerator : public IDWriteFontFileEnumerator {
2431   IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator)
2432 
2433   NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator)
2434 
2435  public:
2436   BundledFontFileEnumerator(IDWriteFactory* aFactory, nsIFile* aFontDir);
2437 
2438   IFACEMETHODIMP MoveNext(BOOL* hasCurrentFile);
2439 
2440   IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile);
2441 
2442  private:
2443   BundledFontFileEnumerator() = delete;
2444   BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete;
2445   BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) =
2446       delete;
2447   virtual ~BundledFontFileEnumerator() = default;
2448 
2449   RefPtr<IDWriteFactory> mFactory;
2450 
2451   nsCOMPtr<nsIFile> mFontDir;
2452   nsCOMPtr<nsIDirectoryEnumerator> mEntries;
2453   nsCOMPtr<nsISupports> mCurrent;
2454 };
2455 
BundledFontFileEnumerator(IDWriteFactory * aFactory,nsIFile * aFontDir)2456 BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory* aFactory,
2457                                                      nsIFile* aFontDir)
2458     : mFactory(aFactory), mFontDir(aFontDir) {
2459   mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries));
2460 }
2461 
2462 IFACEMETHODIMP
MoveNext(BOOL * aHasCurrentFile)2463 BundledFontFileEnumerator::MoveNext(BOOL* aHasCurrentFile) {
2464   bool hasMore = false;
2465   if (mEntries) {
2466     if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) {
2467       if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) {
2468         hasMore = true;
2469       }
2470     }
2471   }
2472   *aHasCurrentFile = hasMore;
2473   return S_OK;
2474 }
2475 
2476 IFACEMETHODIMP
GetCurrentFontFile(IDWriteFontFile ** aFontFile)2477 BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** aFontFile) {
2478   nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent);
2479   if (!file) {
2480     return E_FAIL;
2481   }
2482   nsString path;
2483   if (NS_FAILED(file->GetPath(path))) {
2484     return E_FAIL;
2485   }
2486   return mFactory->CreateFontFileReference((const WCHAR*)path.get(), nullptr,
2487                                            aFontFile);
2488 }
2489 
2490 class BundledFontLoader : public IDWriteFontCollectionLoader {
2491   IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader)
2492 
2493   NS_INLINE_DECL_REFCOUNTING(BundledFontLoader)
2494 
2495  public:
BundledFontLoader()2496   BundledFontLoader() {}
2497 
2498   IFACEMETHODIMP CreateEnumeratorFromKey(
2499       IDWriteFactory* aFactory, const void* aCollectionKey,
2500       UINT32 aCollectionKeySize,
2501       IDWriteFontFileEnumerator** aFontFileEnumerator);
2502 
2503  private:
2504   BundledFontLoader(const BundledFontLoader&) = delete;
2505   BundledFontLoader& operator=(const BundledFontLoader&) = delete;
2506   virtual ~BundledFontLoader() = default;
2507 };
2508 
2509 IFACEMETHODIMP
CreateEnumeratorFromKey(IDWriteFactory * aFactory,const void * aCollectionKey,UINT32 aCollectionKeySize,IDWriteFontFileEnumerator ** aFontFileEnumerator)2510 BundledFontLoader::CreateEnumeratorFromKey(
2511     IDWriteFactory* aFactory, const void* aCollectionKey,
2512     UINT32 aCollectionKeySize,
2513     IDWriteFontFileEnumerator** aFontFileEnumerator) {
2514   nsIFile* fontDir = *(nsIFile**)aCollectionKey;
2515   *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir);
2516   NS_ADDREF(*aFontFileEnumerator);
2517   return S_OK;
2518 }
2519 
2520 already_AddRefed<IDWriteFontCollection>
CreateBundledFontsCollection(IDWriteFactory * aFactory)2521 gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory) {
2522   nsCOMPtr<nsIFile> localDir;
2523   nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2524   if (NS_FAILED(rv)) {
2525     return nullptr;
2526   }
2527   if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
2528     return nullptr;
2529   }
2530   bool isDir;
2531   if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2532     return nullptr;
2533   }
2534 
2535   RefPtr<BundledFontLoader> loader = new BundledFontLoader();
2536   if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) {
2537     return nullptr;
2538   }
2539 
2540   const void* key = localDir.get();
2541   RefPtr<IDWriteFontCollection> collection;
2542   HRESULT hr = aFactory->CreateCustomFontCollection(loader, &key, sizeof(key),
2543                                                     getter_AddRefs(collection));
2544 
2545   aFactory->UnregisterFontCollectionLoader(loader);
2546 
2547   if (FAILED(hr)) {
2548     return nullptr;
2549   } else {
2550     return collection.forget();
2551   }
2552 }
2553 
2554 #endif
2555