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 "gfxFontEntry.h"
7
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/FontPropertyTypes.h"
10 #include "mozilla/MathAlgorithms.h"
11
12 #include "mozilla/Logging.h"
13
14 #include "gfxTextRun.h"
15 #include "gfxPlatform.h"
16 #include "nsGkAtoms.h"
17
18 #include "gfxTypes.h"
19 #include "gfxContext.h"
20 #include "gfxFontConstants.h"
21 #include "gfxGraphiteShaper.h"
22 #include "gfxHarfBuzzShaper.h"
23 #include "gfxUserFontSet.h"
24 #include "gfxPlatformFontList.h"
25 #include "nsUnicodeProperties.h"
26 #include "nsMathUtils.h"
27 #include "nsBidiUtils.h"
28 #include "nsStyleConsts.h"
29 #include "mozilla/AppUnits.h"
30 #include "mozilla/FloatingPoint.h"
31 #ifdef MOZ_WASM_SANDBOXING_GRAPHITE
32 # include "mozilla/ipc/LibrarySandboxPreload.h"
33 #endif
34 #include "mozilla/Likely.h"
35 #include "mozilla/MemoryReporting.h"
36 #include "mozilla/Preferences.h"
37 #include "mozilla/ScopeExit.h"
38 #include "mozilla/Services.h"
39 #include "mozilla/StaticPrefs_layout.h"
40 #include "mozilla/Telemetry.h"
41 #include "gfxSVGGlyphs.h"
42 #include "gfx2DGlue.h"
43
44 #include "harfbuzz/hb.h"
45 #include "harfbuzz/hb-ot.h"
46 #include "graphite2/Font.h"
47
48 #include "ThebesRLBox.h"
49
50 #include <algorithm>
51
52 using namespace mozilla;
53 using namespace mozilla::gfx;
54 using namespace mozilla::unicode;
55 using mozilla::services::GetObserverService;
56
NotifyReleased()57 void gfxCharacterMap::NotifyReleased() {
58 gfxPlatformFontList* fontlist = gfxPlatformFontList::PlatformFontList();
59 if (mShared) {
60 fontlist->RemoveCmap(this);
61 }
62 delete this;
63 }
64
gfxFontEntry()65 gfxFontEntry::gfxFontEntry()
66 : mFixedPitch(false),
67 mIsBadUnderlineFont(false),
68 mIsUserFontContainer(false),
69 mIsDataUserFont(false),
70 mIsLocalUserFont(false),
71 mStandardFace(false),
72 mIgnoreGDEF(false),
73 mIgnoreGSUB(false),
74 mSVGInitialized(false),
75 mHasSpaceFeaturesInitialized(false),
76 mHasSpaceFeatures(false),
77 mHasSpaceFeaturesKerning(false),
78 mHasSpaceFeaturesNonKerning(false),
79 mSkipDefaultFeatureSpaceCheck(false),
80 mGraphiteSpaceContextualsInitialized(false),
81 mHasGraphiteSpaceContextuals(false),
82 mSpaceGlyphIsInvisible(false),
83 mSpaceGlyphIsInvisibleInitialized(false),
84 mHasGraphiteTables(false),
85 mCheckedForGraphiteTables(false),
86 mHasCmapTable(false),
87 mGrFaceInitialized(false),
88 mCheckedForColorGlyph(false),
89 mCheckedForVariationAxes(false) {
90 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
91 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
92 }
93
gfxFontEntry(const nsACString & aName,bool aIsStandardFace)94 gfxFontEntry::gfxFontEntry(const nsACString& aName, bool aIsStandardFace)
95 : mName(aName),
96 mFixedPitch(false),
97 mIsBadUnderlineFont(false),
98 mIsUserFontContainer(false),
99 mIsDataUserFont(false),
100 mIsLocalUserFont(false),
101 mStandardFace(aIsStandardFace),
102 mIgnoreGDEF(false),
103 mIgnoreGSUB(false),
104 mSVGInitialized(false),
105 mHasSpaceFeaturesInitialized(false),
106 mHasSpaceFeatures(false),
107 mHasSpaceFeaturesKerning(false),
108 mHasSpaceFeaturesNonKerning(false),
109 mSkipDefaultFeatureSpaceCheck(false),
110 mGraphiteSpaceContextualsInitialized(false),
111 mHasGraphiteSpaceContextuals(false),
112 mSpaceGlyphIsInvisible(false),
113 mSpaceGlyphIsInvisibleInitialized(false),
114 mHasGraphiteTables(false),
115 mCheckedForGraphiteTables(false),
116 mHasCmapTable(false),
117 mGrFaceInitialized(false),
118 mCheckedForColorGlyph(false),
119 mCheckedForVariationAxes(false) {
120 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
121 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
122 }
123
~gfxFontEntry()124 gfxFontEntry::~gfxFontEntry() {
125 // Should not be dropped by stylo
126 MOZ_ASSERT(NS_IsMainThread());
127 hb_blob_destroy(mCOLR);
128 hb_blob_destroy(mCPAL);
129 if (TrakTableInitialized()) {
130 // Only if it was initialized, so that we don't try to call hb_blob_destroy
131 // on the kTrakTableUninitialized flag value!
132 hb_blob_destroy(mTrakTable);
133 }
134
135 // For downloaded fonts, we need to tell the user font cache that this
136 // entry is being deleted.
137 if (mIsDataUserFont) {
138 gfxUserFontSet::UserFontCache::ForgetFont(this);
139 }
140
141 if (mFeatureInputs) {
142 for (auto iter = mFeatureInputs->Iter(); !iter.Done(); iter.Next()) {
143 hb_set_t*& set = iter.Data();
144 hb_set_destroy(set);
145 }
146 }
147
148 // By the time the entry is destroyed, all font instances that were
149 // using it should already have been deleted, and so the HB and/or Gr
150 // face objects should have been released.
151 MOZ_ASSERT(!mHBFace);
152 MOZ_ASSERT(!mGrFaceInitialized);
153 }
154
TrySetShmemCharacterMap()155 bool gfxFontEntry::TrySetShmemCharacterMap() {
156 MOZ_ASSERT(mShmemFace);
157 auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
158 mShmemCharacterMap =
159 static_cast<const SharedBitSet*>(mShmemFace->mCharacterMap.ToPtr(list));
160 return mShmemCharacterMap != nullptr;
161 }
162
TestCharacterMap(uint32_t aCh)163 bool gfxFontEntry::TestCharacterMap(uint32_t aCh) {
164 if (!mCharacterMap && !mShmemCharacterMap) {
165 ReadCMAP();
166 MOZ_ASSERT(mCharacterMap || mShmemCharacterMap,
167 "failed to initialize character map");
168 }
169 return mShmemCharacterMap ? mShmemCharacterMap->test(aCh)
170 : mCharacterMap->test(aCh);
171 }
172
InitializeUVSMap()173 nsresult gfxFontEntry::InitializeUVSMap() {
174 // mUVSOffset will not be initialized
175 // until cmap is initialized.
176 if (!mCharacterMap && !mShmemCharacterMap) {
177 ReadCMAP();
178 NS_ASSERTION(mCharacterMap || mShmemCharacterMap,
179 "failed to initialize character map");
180 }
181
182 if (!mUVSOffset) {
183 return NS_ERROR_FAILURE;
184 }
185
186 if (!mUVSData) {
187 const uint32_t kCmapTag = TRUETYPE_TAG('c', 'm', 'a', 'p');
188 AutoTable cmapTable(this, kCmapTag);
189 if (!cmapTable) {
190 mUVSOffset = 0; // don't bother to read the table again
191 return NS_ERROR_FAILURE;
192 }
193
194 UniquePtr<uint8_t[]> uvsData;
195 unsigned int cmapLen;
196 const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
197 nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
198 (const uint8_t*)cmapData + mUVSOffset, cmapLen - mUVSOffset, uvsData);
199
200 if (NS_FAILED(rv)) {
201 mUVSOffset = 0; // don't bother to read the table again
202 return rv;
203 }
204
205 mUVSData = std::move(uvsData);
206 }
207
208 return NS_OK;
209 }
210
GetUVSGlyph(uint32_t aCh,uint32_t aVS)211 uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS) {
212 InitializeUVSMap();
213
214 if (mUVSData) {
215 return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData.get(), aCh, aVS);
216 }
217
218 return 0;
219 }
220
SupportsScriptInGSUB(const hb_tag_t * aScriptTags,uint32_t aNumTags)221 bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags,
222 uint32_t aNumTags) {
223 hb_face_t* face = GetHBFace();
224 if (!face) {
225 return false;
226 }
227
228 unsigned int index;
229 hb_tag_t chosenScript;
230 bool found = hb_ot_layout_table_select_script(
231 face, TRUETYPE_TAG('G', 'S', 'U', 'B'), aNumTags, aScriptTags, &index,
232 &chosenScript);
233 hb_face_destroy(face);
234
235 return found && chosenScript != TRUETYPE_TAG('D', 'F', 'L', 'T');
236 }
237
ReadCMAP(FontInfoData * aFontInfoData)238 nsresult gfxFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
239 NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
240 mCharacterMap = new gfxCharacterMap();
241 return NS_OK;
242 }
243
RealFaceName()244 nsCString gfxFontEntry::RealFaceName() {
245 AutoTable nameTable(this, TRUETYPE_TAG('n', 'a', 'm', 'e'));
246 if (nameTable) {
247 nsAutoCString name;
248 nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
249 if (NS_SUCCEEDED(rv)) {
250 return std::move(name);
251 }
252 }
253 return Name();
254 }
255
FindOrMakeFont(const gfxFontStyle * aStyle,gfxCharacterMap * aUnicodeRangeMap)256 gfxFont* gfxFontEntry::FindOrMakeFont(const gfxFontStyle* aStyle,
257 gfxCharacterMap* aUnicodeRangeMap) {
258 // the font entry name is the psname, not the family name
259 gfxFont* font =
260 gfxFontCache::GetCache()->Lookup(this, aStyle, aUnicodeRangeMap);
261
262 if (!font) {
263 gfxFont* newFont = CreateFontInstance(aStyle);
264 if (!newFont) {
265 return nullptr;
266 }
267 if (!newFont->Valid()) {
268 delete newFont;
269 return nullptr;
270 }
271 font = newFont;
272 font->SetUnicodeRangeMap(aUnicodeRangeMap);
273 gfxFontCache::GetCache()->AddNew(font);
274 }
275 return font;
276 }
277
UnitsPerEm()278 uint16_t gfxFontEntry::UnitsPerEm() {
279 if (!mUnitsPerEm) {
280 AutoTable headTable(this, TRUETYPE_TAG('h', 'e', 'a', 'd'));
281 if (headTable) {
282 uint32_t len;
283 const HeadTable* head =
284 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, &len));
285 if (len >= sizeof(HeadTable)) {
286 mUnitsPerEm = head->unitsPerEm;
287 }
288 }
289
290 // if we didn't find a usable 'head' table, or if the value was
291 // outside the valid range, record it as invalid
292 if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
293 mUnitsPerEm = kInvalidUPEM;
294 }
295 }
296 return mUnitsPerEm;
297 }
298
HasSVGGlyph(uint32_t aGlyphId)299 bool gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId) {
300 NS_ASSERTION(mSVGInitialized,
301 "SVG data has not yet been loaded. TryGetSVGData() first.");
302 return mSVGGlyphs->HasSVGGlyph(aGlyphId);
303 }
304
GetSVGGlyphExtents(DrawTarget * aDrawTarget,uint32_t aGlyphId,gfxFloat aSize,gfxRect * aResult)305 bool gfxFontEntry::GetSVGGlyphExtents(DrawTarget* aDrawTarget,
306 uint32_t aGlyphId, gfxFloat aSize,
307 gfxRect* aResult) {
308 MOZ_ASSERT(mSVGInitialized,
309 "SVG data has not yet been loaded. TryGetSVGData() first.");
310 MOZ_ASSERT(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
311 "font has invalid unitsPerEm");
312
313 gfxMatrix svgToApp(aSize / mUnitsPerEm, 0, 0, aSize / mUnitsPerEm, 0, 0);
314 return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToApp, aResult);
315 }
316
RenderSVGGlyph(gfxContext * aContext,uint32_t aGlyphId,SVGContextPaint * aContextPaint)317 void gfxFontEntry::RenderSVGGlyph(gfxContext* aContext, uint32_t aGlyphId,
318 SVGContextPaint* aContextPaint) {
319 NS_ASSERTION(mSVGInitialized,
320 "SVG data has not yet been loaded. TryGetSVGData() first.");
321 mSVGGlyphs->RenderGlyph(aContext, aGlyphId, aContextPaint);
322 }
323
TryGetSVGData(gfxFont * aFont)324 bool gfxFontEntry::TryGetSVGData(gfxFont* aFont) {
325 if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
326 return false;
327 }
328
329 if (!mSVGInitialized) {
330 mSVGInitialized = true;
331
332 // If UnitsPerEm is not known/valid, we can't use SVG glyphs
333 if (UnitsPerEm() == kInvalidUPEM) {
334 return false;
335 }
336
337 // We don't use AutoTable here because we'll pass ownership of this
338 // blob to the gfxSVGGlyphs, once we've confirmed the table exists
339 hb_blob_t* svgTable = GetFontTable(TRUETYPE_TAG('S', 'V', 'G', ' '));
340 if (!svgTable) {
341 return false;
342 }
343
344 // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
345 // with it.
346 mSVGGlyphs = MakeUnique<gfxSVGGlyphs>(svgTable, this);
347 }
348
349 if (mSVGGlyphs && !mFontsUsingSVGGlyphs.Contains(aFont)) {
350 mFontsUsingSVGGlyphs.AppendElement(aFont);
351 }
352
353 return !!mSVGGlyphs;
354 }
355
NotifyFontDestroyed(gfxFont * aFont)356 void gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont) {
357 mFontsUsingSVGGlyphs.RemoveElement(aFont);
358 }
359
NotifyGlyphsChanged()360 void gfxFontEntry::NotifyGlyphsChanged() {
361 for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
362 gfxFont* font = mFontsUsingSVGGlyphs[i];
363 font->NotifyGlyphsChanged();
364 }
365 }
366
TryGetColorGlyphs()367 bool gfxFontEntry::TryGetColorGlyphs() {
368 if (mCheckedForColorGlyph) {
369 return (mCOLR && mCPAL);
370 }
371
372 mCheckedForColorGlyph = true;
373
374 mCOLR = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
375 if (!mCOLR) {
376 return false;
377 }
378
379 mCPAL = GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L'));
380 if (!mCPAL) {
381 hb_blob_destroy(mCOLR);
382 mCOLR = nullptr;
383 return false;
384 }
385
386 // validation COLR and CPAL table
387 if (gfxFontUtils::ValidateColorGlyphs(mCOLR, mCPAL)) {
388 return true;
389 }
390
391 hb_blob_destroy(mCOLR);
392 hb_blob_destroy(mCPAL);
393 mCOLR = nullptr;
394 mCPAL = nullptr;
395 return false;
396 }
397
398 /**
399 * FontTableBlobData
400 *
401 * See FontTableHashEntry for the general strategy.
402 */
403
404 class gfxFontEntry::FontTableBlobData {
405 public:
FontTableBlobData(nsTArray<uint8_t> && aBuffer)406 explicit FontTableBlobData(nsTArray<uint8_t>&& aBuffer)
407 : mTableData(std::move(aBuffer)), mHashtable(nullptr), mHashKey(0) {
408 MOZ_COUNT_CTOR(FontTableBlobData);
409 }
410
~FontTableBlobData()411 ~FontTableBlobData() {
412 MOZ_COUNT_DTOR(FontTableBlobData);
413 if (mHashtable && mHashKey) {
414 mHashtable->RemoveEntry(mHashKey);
415 }
416 }
417
418 // Useful for creating blobs
GetTable() const419 const char* GetTable() const {
420 return reinterpret_cast<const char*>(mTableData.Elements());
421 }
GetTableLength() const422 uint32_t GetTableLength() const { return mTableData.Length(); }
423
424 // Tell this FontTableBlobData to remove the HashEntry when this is
425 // destroyed.
ManageHashEntry(nsTHashtable<FontTableHashEntry> * aHashtable,uint32_t aHashKey)426 void ManageHashEntry(nsTHashtable<FontTableHashEntry>* aHashtable,
427 uint32_t aHashKey) {
428 mHashtable = aHashtable;
429 mHashKey = aHashKey;
430 }
431
432 // Disconnect from the HashEntry (because the blob has already been
433 // removed from the hashtable).
ForgetHashEntry()434 void ForgetHashEntry() {
435 mHashtable = nullptr;
436 mHashKey = 0;
437 }
438
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const439 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
440 return mTableData.ShallowSizeOfExcludingThis(aMallocSizeOf);
441 }
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const442 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
443 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
444 }
445
446 private:
447 // The font table data block
448 nsTArray<uint8_t> mTableData;
449
450 // The blob destroy function needs to know the owning hashtable
451 // and the hashtable key, so that it can remove the entry.
452 nsTHashtable<FontTableHashEntry>* mHashtable;
453 uint32_t mHashKey;
454
455 // not implemented
456 FontTableBlobData(const FontTableBlobData&);
457 };
458
ShareTableAndGetBlob(nsTArray<uint8_t> && aTable,nsTHashtable<FontTableHashEntry> * aHashtable)459 hb_blob_t* gfxFontEntry::FontTableHashEntry::ShareTableAndGetBlob(
460 nsTArray<uint8_t>&& aTable, nsTHashtable<FontTableHashEntry>* aHashtable) {
461 Clear();
462 // adopts elements of aTable
463 mSharedBlobData = new FontTableBlobData(std::move(aTable));
464
465 mBlob = hb_blob_create(
466 mSharedBlobData->GetTable(), mSharedBlobData->GetTableLength(),
467 HB_MEMORY_MODE_READONLY, mSharedBlobData, DeleteFontTableBlobData);
468 if (mBlob == hb_blob_get_empty()) {
469 // The FontTableBlobData was destroyed during hb_blob_create().
470 // The (empty) blob is still be held in the hashtable with a strong
471 // reference.
472 return hb_blob_reference(mBlob);
473 }
474
475 // Tell the FontTableBlobData to remove this hash entry when destroyed.
476 // The hashtable does not keep a strong reference.
477 mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
478 return mBlob;
479 }
480
Clear()481 void gfxFontEntry::FontTableHashEntry::Clear() {
482 // If the FontTableBlobData is managing the hash entry, then the blob is
483 // not owned by this HashEntry; otherwise there is strong reference to the
484 // blob that must be removed.
485 if (mSharedBlobData) {
486 mSharedBlobData->ForgetHashEntry();
487 mSharedBlobData = nullptr;
488 } else {
489 hb_blob_destroy(mBlob);
490 }
491 mBlob = nullptr;
492 }
493
494 // a hb_destroy_func for hb_blob_create
495
496 /* static */
DeleteFontTableBlobData(void * aBlobData)497 void gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(
498 void* aBlobData) {
499 delete static_cast<FontTableBlobData*>(aBlobData);
500 }
501
GetBlob() const502 hb_blob_t* gfxFontEntry::FontTableHashEntry::GetBlob() const {
503 return hb_blob_reference(mBlob);
504 }
505
GetExistingFontTable(uint32_t aTag,hb_blob_t ** aBlob)506 bool gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob) {
507 if (!mFontTableCache) {
508 // we do this here rather than on fontEntry construction
509 // because not all shapers will access the table cache at all
510 mFontTableCache = MakeUnique<nsTHashtable<FontTableHashEntry>>(8);
511 }
512
513 FontTableHashEntry* entry = mFontTableCache->GetEntry(aTag);
514 if (!entry) {
515 return false;
516 }
517
518 *aBlob = entry->GetBlob();
519 return true;
520 }
521
ShareFontTableAndGetBlob(uint32_t aTag,nsTArray<uint8_t> * aBuffer)522 hb_blob_t* gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
523 nsTArray<uint8_t>* aBuffer) {
524 if (MOZ_UNLIKELY(!mFontTableCache)) {
525 // we do this here rather than on fontEntry construction
526 // because not all shapers will access the table cache at all
527 mFontTableCache = MakeUnique<nsTHashtable<FontTableHashEntry>>(8);
528 }
529
530 FontTableHashEntry* entry = mFontTableCache->PutEntry(aTag);
531 if (MOZ_UNLIKELY(!entry)) { // OOM
532 return nullptr;
533 }
534
535 if (!aBuffer) {
536 // ensure the entry is null
537 entry->Clear();
538 return nullptr;
539 }
540
541 return entry->ShareTableAndGetBlob(std::move(*aBuffer),
542 mFontTableCache.get());
543 }
544
GetCMAPFromFontInfo(FontInfoData * aFontInfoData,uint32_t & aUVSOffset)545 already_AddRefed<gfxCharacterMap> gfxFontEntry::GetCMAPFromFontInfo(
546 FontInfoData* aFontInfoData, uint32_t& aUVSOffset) {
547 if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
548 return nullptr;
549 }
550
551 return aFontInfoData->GetCMAP(mName, aUVSOffset);
552 }
553
GetFontTable(uint32_t aTag)554 hb_blob_t* gfxFontEntry::GetFontTable(uint32_t aTag) {
555 hb_blob_t* blob;
556 if (GetExistingFontTable(aTag, &blob)) {
557 return blob;
558 }
559
560 nsTArray<uint8_t> buffer;
561 bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
562
563 return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
564 }
565
566 // callback for HarfBuzz to get a font table (in hb_blob_t form)
567 // from the font entry (passed as aUserData)
568 /*static*/
HBGetTable(hb_face_t * face,uint32_t aTag,void * aUserData)569 hb_blob_t* gfxFontEntry::HBGetTable(hb_face_t* face, uint32_t aTag,
570 void* aUserData) {
571 gfxFontEntry* fontEntry = static_cast<gfxFontEntry*>(aUserData);
572
573 // bug 589682 - ignore the GDEF table in buggy fonts (applies to
574 // Italic and BoldItalic faces of Times New Roman)
575 if (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') && fontEntry->IgnoreGDEF()) {
576 return nullptr;
577 }
578
579 // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
580 // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
581 if (aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') && fontEntry->IgnoreGSUB()) {
582 return nullptr;
583 }
584
585 return fontEntry->GetFontTable(aTag);
586 }
587
588 /*static*/
HBFaceDeletedCallback(void * aUserData)589 void gfxFontEntry::HBFaceDeletedCallback(void* aUserData) {
590 gfxFontEntry* fe = static_cast<gfxFontEntry*>(aUserData);
591 fe->ForgetHBFace();
592 }
593
ForgetHBFace()594 void gfxFontEntry::ForgetHBFace() { mHBFace = nullptr; }
595
GetHBFace()596 hb_face_t* gfxFontEntry::GetHBFace() {
597 if (!mHBFace) {
598 mHBFace =
599 hb_face_create_for_tables(HBGetTable, this, HBFaceDeletedCallback);
600 return mHBFace;
601 }
602 return hb_face_reference(mHBFace);
603 }
604
605 struct gfxFontEntry::GrSandboxData {
606 rlbox_sandbox_gr sandbox;
607 sandbox_callback_gr<const void* (*)(const void*, unsigned int, size_t*)>
608 grGetTableCallback;
609 sandbox_callback_gr<void (*)(const void*, const void*)>
610 grReleaseTableCallback;
611 // Text Shapers register a callback to get glyph advances
612 sandbox_callback_gr<float (*)(const void*, uint16_t)>
613 grGetGlyphAdvanceCallback;
614
GrSandboxDatagfxFontEntry::GrSandboxData615 GrSandboxData() {
616 #ifdef MOZ_WASM_SANDBOXING_GRAPHITE
617 // Firefox preloads the library externally to ensure we won't be stopped by
618 // the content sandbox
619 const bool external_loads_exist = true;
620 // See Bug 1606981: In some environments allowing stdio in the wasm sandbox
621 // fails as the I/O redirection involves querying meta-data of file
622 // descriptors. This querying fails in some environments.
623 const bool allow_stdio = false;
624 sandbox.create_sandbox(mozilla::ipc::GetSandboxedGraphitePath().get(),
625 external_loads_exist, allow_stdio);
626 #else
627 sandbox.create_sandbox();
628 #endif
629 grGetTableCallback = sandbox.register_callback(GrGetTable);
630 grReleaseTableCallback = sandbox.register_callback(GrReleaseTable);
631 grGetGlyphAdvanceCallback =
632 sandbox.register_callback(gfxGraphiteShaper::GrGetAdvance);
633 }
634
~GrSandboxDatagfxFontEntry::GrSandboxData635 ~GrSandboxData() {
636 grGetTableCallback.unregister();
637 grReleaseTableCallback.unregister();
638 grGetGlyphAdvanceCallback.unregister();
639 sandbox.destroy_sandbox();
640 }
641 };
642
643 static thread_local gfxFontEntry* tl_grGetFontTableCallbackData = nullptr;
644
645 /*static*/
GrGetTable(rlbox_sandbox_gr & sandbox,tainted_opaque_gr<const void * >,tainted_opaque_gr<unsigned int> aName,tainted_opaque_gr<size_t * > aLen)646 tainted_opaque_gr<const void*> gfxFontEntry::GrGetTable(
647 rlbox_sandbox_gr& sandbox,
648 tainted_opaque_gr<const void*> /* aAppFaceHandle */,
649 tainted_opaque_gr<unsigned int> aName, tainted_opaque_gr<size_t*> aLen) {
650 gfxFontEntry* fontEntry = tl_grGetFontTableCallbackData;
651 tainted_gr<size_t*> t_aLen = rlbox::from_opaque(aLen);
652 *t_aLen = 0;
653 tainted_gr<const void*> ret = nullptr;
654
655 if (fontEntry) {
656 unsigned int fontTableKey =
657 rlbox::from_opaque(aName).unverified_safe_because(
658 "This is only being used to index into a hashmap, which is robust "
659 "for any value. No checks needed.");
660 hb_blob_t* blob = fontEntry->GetFontTable(fontTableKey);
661
662 if (blob) {
663 unsigned int blobLength;
664 const void* tableData = hb_blob_get_data(blob, &blobLength);
665 // tableData is read-only data shared with the sandbox.
666 // Making a copy in sandbox memory
667 tainted_gr<void*> t_tableData = rlbox::sandbox_reinterpret_cast<void*>(
668 sandbox.malloc_in_sandbox<char>(blobLength));
669 if (t_tableData) {
670 rlbox::memcpy(sandbox, t_tableData, tableData, blobLength);
671 *t_aLen = blobLength;
672 ret = rlbox::sandbox_const_cast<const void*>(t_tableData);
673 }
674 hb_blob_destroy(blob);
675 }
676 }
677
678 return ret.to_opaque();
679 }
680
681 /*static*/
GrReleaseTable(rlbox_sandbox_gr & sandbox,tainted_opaque_gr<const void * >,tainted_opaque_gr<const void * > aTableBuffer)682 void gfxFontEntry::GrReleaseTable(
683 rlbox_sandbox_gr& sandbox,
684 tainted_opaque_gr<const void*> /* aAppFaceHandle */,
685 tainted_opaque_gr<const void*> aTableBuffer) {
686 sandbox.free_in_sandbox(rlbox::from_opaque(aTableBuffer));
687 }
688
GetGrSandbox()689 rlbox_sandbox_gr* gfxFontEntry::GetGrSandbox() {
690 MOZ_ASSERT(mSandboxData != nullptr);
691 return &mSandboxData->sandbox;
692 }
693
694 sandbox_callback_gr<float (*)(const void*, uint16_t)>*
GetGrSandboxAdvanceCallbackHandle()695 gfxFontEntry::GetGrSandboxAdvanceCallbackHandle() {
696 MOZ_ASSERT(mSandboxData != nullptr);
697 return &mSandboxData->grGetGlyphAdvanceCallback;
698 }
699
GetGrFace()700 tainted_opaque_gr<gr_face*> gfxFontEntry::GetGrFace() {
701 if (!mGrFaceInitialized) {
702 // When possible, the below code will use WASM as a sandboxing mechanism.
703 // At this time the wasm sandbox does not support threads.
704 // If Thebes is updated to make callst to the sandbox on multiple threaads,
705 // we need to make sure the underlying sandbox supports threading.
706 MOZ_ASSERT(NS_IsMainThread());
707
708 mSandboxData = new GrSandboxData();
709
710 auto p_faceOps = mSandboxData->sandbox.malloc_in_sandbox<gr_face_ops>();
711 if (!p_faceOps) {
712 MOZ_CRASH("Graphite sandbox memory allocation failed");
713 }
714 auto cleanup = MakeScopeExit(
715 [&] { mSandboxData->sandbox.free_in_sandbox(p_faceOps); });
716 p_faceOps->size = sizeof(*p_faceOps);
717 p_faceOps->get_table = mSandboxData->grGetTableCallback;
718 p_faceOps->release_table = mSandboxData->grReleaseTableCallback;
719
720 tl_grGetFontTableCallbackData = this;
721 auto face = sandbox_invoke(
722 mSandboxData->sandbox, gr_make_face_with_ops,
723 // For security, we do not pass the callback data to this arg, and use
724 // a TLS var instead. However, gr_make_face_with_ops expects this to
725 // be a non null ptr. Therefore, we should pass some dummy non null
726 // pointer which will be passed to callbacks, but never used. Let's just
727 // pass p_faceOps again, as this is a non-null tainted pointer.
728 p_faceOps /* appFaceHandle */, p_faceOps, gr_face_default);
729 tl_grGetFontTableCallbackData = nullptr;
730 mGrFace = face.to_opaque();
731 mGrFaceInitialized = true;
732 }
733 ++mGrFaceRefCnt;
734 return mGrFace;
735 }
736
ReleaseGrFace(tainted_opaque_gr<gr_face * > aFace)737 void gfxFontEntry::ReleaseGrFace(tainted_opaque_gr<gr_face*> aFace) {
738 MOZ_ASSERT(
739 (rlbox::from_opaque(aFace) == rlbox::from_opaque(mGrFace))
740 .unverified_safe_because(
741 "This is safe as the only thing we are doing is comparing "
742 "addresses of two tainted pointers. Furthermore this is used "
743 "merely as a debugging aid in the debug builds. This function is "
744 "called only from the trusted Firefox code rather than the "
745 "untrusted libGraphite.")); // sanity-check
746 MOZ_ASSERT(mGrFaceRefCnt > 0);
747 if (--mGrFaceRefCnt == 0) {
748 auto t_mGrFace = rlbox::from_opaque(mGrFace);
749
750 tl_grGetFontTableCallbackData = this;
751 sandbox_invoke(mSandboxData->sandbox, gr_face_destroy, t_mGrFace);
752 tl_grGetFontTableCallbackData = nullptr;
753
754 t_mGrFace = nullptr;
755 mGrFace = t_mGrFace.to_opaque();
756
757 delete mSandboxData;
758 mSandboxData = nullptr;
759
760 mGrFaceInitialized = false;
761 }
762 }
763
DisconnectSVG()764 void gfxFontEntry::DisconnectSVG() {
765 if (mSVGInitialized && mSVGGlyphs) {
766 mSVGGlyphs = nullptr;
767 mSVGInitialized = false;
768 }
769 }
770
HasFontTable(uint32_t aTableTag)771 bool gfxFontEntry::HasFontTable(uint32_t aTableTag) {
772 AutoTable table(this, aTableTag);
773 return table && hb_blob_get_length(table) > 0;
774 }
775
CheckForGraphiteTables()776 void gfxFontEntry::CheckForGraphiteTables() {
777 mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S', 'i', 'l', 'f'));
778 }
779
HasGraphiteSpaceContextuals()780 tainted_boolean_hint gfxFontEntry::HasGraphiteSpaceContextuals() {
781 if (!mGraphiteSpaceContextualsInitialized) {
782 auto face = GetGrFace();
783 auto t_face = rlbox::from_opaque(face);
784 if (t_face) {
785 tainted_gr<const gr_faceinfo*> faceInfo =
786 sandbox_invoke(mSandboxData->sandbox, gr_face_info, t_face, 0);
787 // Comparison with a value in sandboxed memory returns a
788 // tainted_boolean_hint, i.e. a "hint", since the value could be changed
789 // maliciously at any moment.
790 tainted_boolean_hint is_not_none =
791 faceInfo->space_contextuals != gr_faceinfo::gr_space_none;
792 mHasGraphiteSpaceContextuals = is_not_none.unverified_safe_because(
793 "Note ideally mHasGraphiteSpaceContextuals would be "
794 "tainted_boolean_hint, but RLBox does not yet support bitfields, so "
795 "it is not wrapped. However, its value is only ever accessed through "
796 "this function which returns a tainted_boolean_hint, so unwrapping "
797 "temporarily is safe. We remove the wrapper now and re-add it "
798 "below.");
799 }
800 ReleaseGrFace(face); // always balance GetGrFace, even if face is null
801 mGraphiteSpaceContextualsInitialized = true;
802 }
803
804 bool ret = mHasGraphiteSpaceContextuals;
805 return tainted_boolean_hint(ret);
806 }
807
808 #define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag
809
810 static_assert(int(Script::NUM_SCRIPT_CODES) <= FEATURE_SCRIPT_MASK,
811 "Too many script codes");
812
813 // high-order three bytes of tag with script in low-order byte
814 #define SCRIPT_FEATURE(s, tag) \
815 (((~FEATURE_SCRIPT_MASK) & (tag)) | \
816 ((FEATURE_SCRIPT_MASK) & static_cast<uint32_t>(s)))
817
SupportsOpenTypeFeature(Script aScript,uint32_t aFeatureTag)818 bool gfxFontEntry::SupportsOpenTypeFeature(Script aScript,
819 uint32_t aFeatureTag) {
820 if (!mSupportedFeatures) {
821 mSupportedFeatures = MakeUnique<nsDataHashtable<nsUint32HashKey, bool>>();
822 }
823
824 // note: high-order three bytes *must* be unique for each feature
825 // listed below (see SCRIPT_FEATURE macro def'n)
826 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'm', 'c', 'p') ||
827 aFeatureTag == HB_TAG('c', '2', 's', 'c') ||
828 aFeatureTag == HB_TAG('p', 'c', 'a', 'p') ||
829 aFeatureTag == HB_TAG('c', '2', 'p', 'c') ||
830 aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
831 aFeatureTag == HB_TAG('s', 'u', 'b', 's') ||
832 aFeatureTag == HB_TAG('v', 'e', 'r', 't'),
833 "use of unknown feature tag");
834
835 // note: graphite feature support uses the last script index
836 NS_ASSERTION(int(aScript) < FEATURE_SCRIPT_MASK - 1,
837 "need to bump the size of the feature shift");
838
839 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
840 bool result;
841 if (mSupportedFeatures->Get(scriptFeature, &result)) {
842 return result;
843 }
844
845 result = false;
846
847 hb_face_t* face = GetHBFace();
848
849 if (hb_ot_layout_has_substitution(face)) {
850 hb_script_t hbScript =
851 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
852
853 // Get the OpenType tag(s) that match this script code
854 unsigned int scriptCount = 4;
855 hb_tag_t scriptTags[4];
856 hb_ot_tags_from_script_and_language(hbScript, HB_LANGUAGE_INVALID,
857 &scriptCount, scriptTags, nullptr,
858 nullptr);
859
860 // Append DEFAULT to the returned tags, if room
861 if (scriptCount < 4) {
862 scriptTags[scriptCount++] = HB_OT_TAG_DEFAULT_SCRIPT;
863 }
864
865 // Now check for 'smcp' under the first of those scripts that is present
866 const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
867 for (unsigned int i = 0; i < scriptCount; i++) {
868 unsigned int scriptIndex;
869 if (hb_ot_layout_table_find_script(face, kGSUB, scriptTags[i],
870 &scriptIndex)) {
871 if (hb_ot_layout_language_find_feature(
872 face, kGSUB, scriptIndex, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
873 aFeatureTag, nullptr)) {
874 result = true;
875 }
876 break;
877 }
878 }
879 }
880
881 hb_face_destroy(face);
882
883 mSupportedFeatures->Put(scriptFeature, result);
884
885 return result;
886 }
887
InputsForOpenTypeFeature(Script aScript,uint32_t aFeatureTag)888 const hb_set_t* gfxFontEntry::InputsForOpenTypeFeature(Script aScript,
889 uint32_t aFeatureTag) {
890 if (!mFeatureInputs) {
891 mFeatureInputs = MakeUnique<nsDataHashtable<nsUint32HashKey, hb_set_t*>>();
892 }
893
894 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
895 aFeatureTag == HB_TAG('s', 'u', 'b', 's') ||
896 aFeatureTag == HB_TAG('v', 'e', 'r', 't'),
897 "use of unknown feature tag");
898
899 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
900 hb_set_t* inputGlyphs;
901 if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
902 return inputGlyphs;
903 }
904
905 inputGlyphs = hb_set_create();
906
907 hb_face_t* face = GetHBFace();
908
909 if (hb_ot_layout_has_substitution(face)) {
910 hb_script_t hbScript =
911 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
912
913 // Get the OpenType tag(s) that match this script code
914 unsigned int scriptCount = 4;
915 hb_tag_t scriptTags[5]; // space for null terminator
916 hb_ot_tags_from_script_and_language(hbScript, HB_LANGUAGE_INVALID,
917 &scriptCount, scriptTags, nullptr,
918 nullptr);
919
920 // Append DEFAULT to the returned tags, if room
921 if (scriptCount < 4) {
922 scriptTags[scriptCount++] = HB_OT_TAG_DEFAULT_SCRIPT;
923 }
924 scriptTags[scriptCount++] = 0;
925
926 const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
927 hb_tag_t features[2] = {aFeatureTag, HB_TAG_NONE};
928 hb_set_t* featurelookups = hb_set_create();
929 hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr, features,
930 featurelookups);
931 hb_codepoint_t index = -1;
932 while (hb_set_next(featurelookups, &index)) {
933 hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index, nullptr,
934 inputGlyphs, nullptr, nullptr);
935 }
936 hb_set_destroy(featurelookups);
937 }
938
939 hb_face_destroy(face);
940
941 mFeatureInputs->Put(scriptFeature, inputGlyphs);
942 return inputGlyphs;
943 }
944
SupportsGraphiteFeature(uint32_t aFeatureTag)945 bool gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag) {
946 if (!mSupportedFeatures) {
947 mSupportedFeatures = MakeUnique<nsDataHashtable<nsUint32HashKey, bool>>();
948 }
949
950 // note: high-order three bytes *must* be unique for each feature
951 // listed below (see SCRIPT_FEATURE macro def'n)
952 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'm', 'c', 'p') ||
953 aFeatureTag == HB_TAG('c', '2', 's', 'c') ||
954 aFeatureTag == HB_TAG('p', 'c', 'a', 'p') ||
955 aFeatureTag == HB_TAG('c', '2', 'p', 'c') ||
956 aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
957 aFeatureTag == HB_TAG('s', 'u', 'b', 's'),
958 "use of unknown feature tag");
959
960 // graphite feature check uses the last script slot
961 uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag);
962 bool result;
963 if (mSupportedFeatures->Get(scriptFeature, &result)) {
964 return result;
965 }
966
967 auto face = GetGrFace();
968 auto t_face = rlbox::from_opaque(face);
969 result = t_face ? sandbox_invoke(mSandboxData->sandbox, gr_face_find_fref,
970 t_face, aFeatureTag) != nullptr
971 : false;
972 ReleaseGrFace(face);
973
974 mSupportedFeatures->Put(scriptFeature, result);
975
976 return result;
977 }
978
GetFeatureInfo(nsTArray<gfxFontFeatureInfo> & aFeatureInfo)979 void gfxFontEntry::GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo) {
980 // TODO: implement alternative code path for graphite fonts
981
982 hb_face_t* face = GetHBFace();
983
984 // Get the list of features for a specific <script,langSys> pair and
985 // append them to aFeatureInfo.
986 auto collectForLang = [=, &aFeatureInfo](
987 hb_tag_t aTableTag, unsigned int aScript,
988 hb_tag_t aScriptTag, unsigned int aLang,
989 hb_tag_t aLangTag) {
990 unsigned int featCount = hb_ot_layout_language_get_feature_tags(
991 face, aTableTag, aScript, aLang, 0, nullptr, nullptr);
992 AutoTArray<hb_tag_t, 32> featTags;
993 featTags.SetLength(featCount);
994 hb_ot_layout_language_get_feature_tags(face, aTableTag, aScript, aLang, 0,
995 &featCount, featTags.Elements());
996 MOZ_ASSERT(featCount <= featTags.Length());
997 // Just in case HB didn't fill featTags (i.e. in case it returned fewer
998 // tags than it promised), we truncate at the length it says it filled:
999 featTags.SetLength(featCount);
1000 for (hb_tag_t t : featTags) {
1001 aFeatureInfo.AppendElement(gfxFontFeatureInfo{t, aScriptTag, aLangTag});
1002 }
1003 };
1004
1005 // Iterate over the language systems supported by a given script,
1006 // and call collectForLang for each of them.
1007 auto collectForScript = [=](hb_tag_t aTableTag, unsigned int aScript,
1008 hb_tag_t aScriptTag) {
1009 collectForLang(aTableTag, aScript, aScriptTag,
1010 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
1011 HB_TAG('d', 'f', 'l', 't'));
1012 unsigned int langCount = hb_ot_layout_script_get_language_tags(
1013 face, aTableTag, aScript, 0, nullptr, nullptr);
1014 AutoTArray<hb_tag_t, 32> langTags;
1015 langTags.SetLength(langCount);
1016 hb_ot_layout_script_get_language_tags(face, aTableTag, aScript, 0,
1017 &langCount, langTags.Elements());
1018 MOZ_ASSERT(langCount <= langTags.Length());
1019 langTags.SetLength(langCount);
1020 for (unsigned int lang = 0; lang < langCount; ++lang) {
1021 collectForLang(aTableTag, aScript, aScriptTag, lang, langTags[lang]);
1022 }
1023 };
1024
1025 // Iterate over the scripts supported by a table (GSUB or GPOS), and call
1026 // collectForScript for each of them.
1027 auto collectForTable = [=](hb_tag_t aTableTag) {
1028 unsigned int scriptCount = hb_ot_layout_table_get_script_tags(
1029 face, aTableTag, 0, nullptr, nullptr);
1030 AutoTArray<hb_tag_t, 32> scriptTags;
1031 scriptTags.SetLength(scriptCount);
1032 hb_ot_layout_table_get_script_tags(face, aTableTag, 0, &scriptCount,
1033 scriptTags.Elements());
1034 MOZ_ASSERT(scriptCount <= scriptTags.Length());
1035 scriptTags.SetLength(scriptCount);
1036 for (unsigned int script = 0; script < scriptCount; ++script) {
1037 collectForScript(aTableTag, script, scriptTags[script]);
1038 }
1039 };
1040
1041 // Collect all OpenType Layout features, both substitution and positioning,
1042 // supported by the font resource.
1043 collectForTable(HB_TAG('G', 'S', 'U', 'B'));
1044 collectForTable(HB_TAG('G', 'P', 'O', 'S'));
1045
1046 hb_face_destroy(face);
1047 }
1048
GetColorLayersInfo(uint32_t aGlyphId,const mozilla::gfx::DeviceColor & aDefaultColor,nsTArray<uint16_t> & aLayerGlyphs,nsTArray<mozilla::gfx::DeviceColor> & aLayerColors)1049 bool gfxFontEntry::GetColorLayersInfo(
1050 uint32_t aGlyphId, const mozilla::gfx::DeviceColor& aDefaultColor,
1051 nsTArray<uint16_t>& aLayerGlyphs,
1052 nsTArray<mozilla::gfx::DeviceColor>& aLayerColors) {
1053 return gfxFontUtils::GetColorGlyphLayers(
1054 mCOLR, mCPAL, aGlyphId, aDefaultColor, aLayerGlyphs, aLayerColors);
1055 }
1056
1057 typedef struct {
1058 AutoSwap_PRUint32 version;
1059 AutoSwap_PRUint16 format;
1060 AutoSwap_PRUint16 horizOffset;
1061 AutoSwap_PRUint16 vertOffset;
1062 AutoSwap_PRUint16 reserved;
1063 // TrackData horizData;
1064 // TrackData vertData;
1065 } TrakHeader;
1066
1067 typedef struct {
1068 AutoSwap_PRUint16 nTracks;
1069 AutoSwap_PRUint16 nSizes;
1070 AutoSwap_PRUint32 sizeTableOffset;
1071 // trackTableEntry trackTable[];
1072 // fixed32 sizeTable[];
1073 } TrackData;
1074
1075 typedef struct {
1076 AutoSwap_PRUint32 track;
1077 AutoSwap_PRUint16 nameIndex;
1078 AutoSwap_PRUint16 offset;
1079 } TrackTableEntry;
1080
HasTrackingTable()1081 bool gfxFontEntry::HasTrackingTable() {
1082 if (!TrakTableInitialized()) {
1083 mTrakTable = GetFontTable(TRUETYPE_TAG('t', 'r', 'a', 'k'));
1084 if (mTrakTable) {
1085 if (!ParseTrakTable()) {
1086 hb_blob_destroy(mTrakTable);
1087 mTrakTable = nullptr;
1088 }
1089 }
1090 }
1091 return mTrakTable != nullptr;
1092 }
1093
ParseTrakTable()1094 bool gfxFontEntry::ParseTrakTable() {
1095 // Check table validity and set up the subtable pointers we need;
1096 // if 'trak' table is invalid, or doesn't contain a 'normal' track,
1097 // return false to tell the caller not to try using it.
1098 unsigned int len;
1099 const char* data = hb_blob_get_data(mTrakTable, &len);
1100 if (len < sizeof(TrakHeader)) {
1101 return false;
1102 }
1103 auto trak = reinterpret_cast<const TrakHeader*>(data);
1104 uint16_t horizOffset = trak->horizOffset;
1105 if (trak->version != 0x00010000 || uint16_t(trak->format) != 0 ||
1106 horizOffset == 0 || uint16_t(trak->reserved) != 0) {
1107 return false;
1108 }
1109 // Find the horizontal trackData, and check it doesn't overrun the buffer.
1110 if (horizOffset > len - sizeof(TrackData)) {
1111 return false;
1112 }
1113 auto trackData = reinterpret_cast<const TrackData*>(data + horizOffset);
1114 uint16_t nTracks = trackData->nTracks;
1115 mNumTrakSizes = trackData->nSizes;
1116 if (nTracks == 0 || mNumTrakSizes < 2) {
1117 return false;
1118 }
1119 uint32_t sizeTableOffset = trackData->sizeTableOffset;
1120 // Find the trackTable, and check it doesn't overrun the buffer.
1121 if (horizOffset >
1122 len - (sizeof(TrackData) + nTracks * sizeof(TrackTableEntry))) {
1123 return false;
1124 }
1125 auto trackTable = reinterpret_cast<const TrackTableEntry*>(
1126 data + horizOffset + sizeof(TrackData));
1127 // Look for 'normal' tracking, bail out if no such track is present.
1128 unsigned trackIndex;
1129 for (trackIndex = 0; trackIndex < nTracks; ++trackIndex) {
1130 if (trackTable[trackIndex].track == 0x00000000) {
1131 break;
1132 }
1133 }
1134 if (trackIndex == nTracks) {
1135 return false;
1136 }
1137 // Find list of tracking values, and check they won't overrun.
1138 uint16_t offset = trackTable[trackIndex].offset;
1139 if (offset > len - mNumTrakSizes * sizeof(uint16_t)) {
1140 return false;
1141 }
1142 mTrakValues = reinterpret_cast<const AutoSwap_PRInt16*>(data + offset);
1143 // Find the size subtable, and check it doesn't overrun the buffer.
1144 mTrakSizeTable =
1145 reinterpret_cast<const AutoSwap_PRInt32*>(data + sizeTableOffset);
1146 if (mTrakSizeTable + mNumTrakSizes >
1147 reinterpret_cast<const AutoSwap_PRInt32*>(data + len)) {
1148 return false;
1149 }
1150 return true;
1151 }
1152
TrackingForCSSPx(float aSize) const1153 float gfxFontEntry::TrackingForCSSPx(float aSize) const {
1154 MOZ_ASSERT(TrakTableInitialized() && mTrakTable && mTrakValues &&
1155 mTrakSizeTable);
1156
1157 // Find index of first sizeTable entry that is >= the requested size.
1158 int32_t fixedSize = int32_t(aSize * 65536.0); // float -> 16.16 fixed-point
1159 unsigned sizeIndex;
1160 for (sizeIndex = 0; sizeIndex < mNumTrakSizes; ++sizeIndex) {
1161 if (mTrakSizeTable[sizeIndex] >= fixedSize) {
1162 break;
1163 }
1164 }
1165 // Return the tracking value for the requested size, or an interpolated
1166 // value if the exact size isn't found.
1167 if (sizeIndex == mNumTrakSizes) {
1168 // Request is larger than last entry in the table, so just use that.
1169 // (We don't attempt to extrapolate more extreme tracking values than
1170 // the largest or smallest present in the table.)
1171 return int16_t(mTrakValues[mNumTrakSizes - 1]);
1172 }
1173 if (sizeIndex == 0 || mTrakSizeTable[sizeIndex] == fixedSize) {
1174 // Found an exact match, or size was smaller than the first entry.
1175 return int16_t(mTrakValues[sizeIndex]);
1176 }
1177 // Requested size falls between two entries: interpolate value.
1178 double s0 = mTrakSizeTable[sizeIndex - 1] / 65536.0; // 16.16 -> float
1179 double s1 = mTrakSizeTable[sizeIndex] / 65536.0;
1180 double t = (aSize - s0) / (s1 - s0);
1181 return (1.0 - t) * int16_t(mTrakValues[sizeIndex - 1]) +
1182 t * int16_t(mTrakValues[sizeIndex]);
1183 }
1184
SetupVariationRanges()1185 void gfxFontEntry::SetupVariationRanges() {
1186 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport() ||
1187 !StaticPrefs::layout_css_font_variations_enabled() || !HasVariations() ||
1188 IsUserFont()) {
1189 return;
1190 }
1191 AutoTArray<gfxFontVariationAxis, 4> axes;
1192 GetVariationAxes(axes);
1193 for (const auto& axis : axes) {
1194 switch (axis.mTag) {
1195 case HB_TAG('w', 'g', 'h', 't'):
1196 // If the axis range looks like it doesn't fit the CSS font-weight
1197 // scale, we don't hook up the high-level property, and we mark
1198 // the face (in mRangeFlags) as having non-standard weight. This
1199 // means we won't map CSS font-weight to the axis. Setting 'wght'
1200 // with font-variation-settings will still work.
1201 // Strictly speaking, the min value should be checked against 1.0,
1202 // not 0.0, but we'll allow font makers that amount of leeway, as
1203 // in practice a number of fonts seem to use 0..1000.
1204 if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
1205 // If axis.mMaxValue is less than the default weight we already
1206 // set up, assume the axis has a non-standard range (like Skia)
1207 // and don't try to map it.
1208 Weight().Min() <= FontWeight(axis.mMaxValue)) {
1209 if (FontWeight(axis.mDefaultValue) != Weight().Min()) {
1210 mStandardFace = false;
1211 }
1212 mWeightRange = WeightRange(FontWeight(std::max(1.0f, axis.mMinValue)),
1213 FontWeight(axis.mMaxValue));
1214 } else {
1215 mRangeFlags |= RangeFlags::eNonCSSWeight;
1216 }
1217 break;
1218
1219 case HB_TAG('w', 'd', 't', 'h'):
1220 if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
1221 Stretch().Min() <= FontStretch(axis.mMaxValue)) {
1222 if (FontStretch(axis.mDefaultValue) != Stretch().Min()) {
1223 mStandardFace = false;
1224 }
1225 mStretchRange = StretchRange(FontStretch(axis.mMinValue),
1226 FontStretch(axis.mMaxValue));
1227 } else {
1228 mRangeFlags |= RangeFlags::eNonCSSStretch;
1229 }
1230 break;
1231
1232 case HB_TAG('s', 'l', 'n', 't'):
1233 if (axis.mMinValue >= -90.0f && axis.mMaxValue <= 90.0f) {
1234 if (FontSlantStyle::Oblique(axis.mDefaultValue) !=
1235 SlantStyle().Min()) {
1236 mStandardFace = false;
1237 }
1238 // OpenType and CSS measure angles in opposite directions, so we
1239 // have to flip signs and swap min/max when setting up the CSS
1240 // font-style range here.
1241 mStyleRange =
1242 SlantStyleRange(FontSlantStyle::Oblique(-axis.mMaxValue),
1243 FontSlantStyle::Oblique(-axis.mMinValue));
1244 }
1245 break;
1246
1247 case HB_TAG('i', 't', 'a', 'l'):
1248 if (axis.mMinValue <= 0.0f && axis.mMaxValue >= 1.0f) {
1249 if (axis.mDefaultValue != 0.0f) {
1250 mStandardFace = false;
1251 }
1252 mStyleRange = SlantStyleRange(FontSlantStyle::Normal(),
1253 FontSlantStyle::Italic());
1254 }
1255 break;
1256
1257 default:
1258 continue;
1259 }
1260 }
1261 }
1262
CheckForVariationAxes()1263 void gfxFontEntry::CheckForVariationAxes() {
1264 if (HasVariations()) {
1265 AutoTArray<gfxFontVariationAxis, 4> axes;
1266 GetVariationAxes(axes);
1267 for (const auto& axis : axes) {
1268 if (axis.mTag == HB_TAG('w', 'g', 'h', 't') && axis.mMaxValue >= 600.0f) {
1269 mRangeFlags |= RangeFlags::eBoldVariableWeight;
1270 } else if (axis.mTag == HB_TAG('i', 't', 'a', 'l') &&
1271 axis.mMaxValue >= 1.0f) {
1272 mRangeFlags |= RangeFlags::eItalicVariation;
1273 }
1274 }
1275 }
1276 mCheckedForVariationAxes = true;
1277 }
1278
HasBoldVariableWeight()1279 bool gfxFontEntry::HasBoldVariableWeight() {
1280 MOZ_ASSERT(!mIsUserFontContainer,
1281 "should not be called for user-font containers!");
1282
1283 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
1284 return false;
1285 }
1286
1287 if (!mCheckedForVariationAxes) {
1288 CheckForVariationAxes();
1289 }
1290
1291 return bool(mRangeFlags & RangeFlags::eBoldVariableWeight);
1292 }
1293
HasItalicVariation()1294 bool gfxFontEntry::HasItalicVariation() {
1295 MOZ_ASSERT(!mIsUserFontContainer,
1296 "should not be called for user-font containers!");
1297
1298 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
1299 return false;
1300 }
1301
1302 if (!mCheckedForVariationAxes) {
1303 CheckForVariationAxes();
1304 }
1305
1306 return bool(mRangeFlags & RangeFlags::eItalicVariation);
1307 }
1308
GetVariationsForStyle(nsTArray<gfxFontVariation> & aResult,const gfxFontStyle & aStyle)1309 void gfxFontEntry::GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
1310 const gfxFontStyle& aStyle) {
1311 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport() ||
1312 !StaticPrefs::layout_css_font_variations_enabled()) {
1313 return;
1314 }
1315
1316 if (!HasVariations()) {
1317 return;
1318 }
1319
1320 // Resolve high-level CSS properties from the requested style
1321 // (font-{style,weight,stretch}) to the appropriate variations.
1322 // The value used is clamped to the range available in the font face,
1323 // unless the face is a user font where no explicit descriptor was
1324 // given, indicated by the corresponding 'auto' range-flag.
1325
1326 // We don't do these mappings if the font entry has weight and/or stretch
1327 // ranges that do not appear to use the CSS property scale. Some older
1328 // fonts created for QuickDrawGX/AAT may use "normalized" values where the
1329 // standard variation is 1.0 rather than 400.0 (weight) or 100.0 (stretch).
1330
1331 if (!(mRangeFlags & RangeFlags::eNonCSSWeight)) {
1332 float weight = (IsUserFont() && (mRangeFlags & RangeFlags::eAutoWeight))
1333 ? aStyle.weight.ToFloat()
1334 : Weight().Clamp(aStyle.weight).ToFloat();
1335 aResult.AppendElement(gfxFontVariation{HB_TAG('w', 'g', 'h', 't'), weight});
1336 }
1337
1338 if (!(mRangeFlags & RangeFlags::eNonCSSStretch)) {
1339 float stretch = (IsUserFont() && (mRangeFlags & RangeFlags::eAutoStretch))
1340 ? aStyle.stretch.Percentage()
1341 : Stretch().Clamp(aStyle.stretch).Percentage();
1342 aResult.AppendElement(
1343 gfxFontVariation{HB_TAG('w', 'd', 't', 'h'), stretch});
1344 }
1345
1346 if (aStyle.style.IsItalic() && SupportsItalic()) {
1347 // The 'ital' axis is normally a binary toggle; intermediate values
1348 // can only be set using font-variation-settings.
1349 aResult.AppendElement(gfxFontVariation{HB_TAG('i', 't', 'a', 'l'), 1.0f});
1350 } else if (SlantStyle().Min().IsOblique()) {
1351 // Figure out what slant angle we should try to match from the
1352 // requested style.
1353 float angle = aStyle.style.IsNormal()
1354 ? 0.0f
1355 : aStyle.style.IsItalic()
1356 ? FontSlantStyle::Oblique().ObliqueAngle()
1357 : aStyle.style.ObliqueAngle();
1358 // Clamp to the available range, unless the face is a user font
1359 // with no explicit descriptor.
1360 if (!(IsUserFont() && (mRangeFlags & RangeFlags::eAutoSlantStyle))) {
1361 angle = SlantStyle().Clamp(FontSlantStyle::Oblique(angle)).ObliqueAngle();
1362 }
1363 // OpenType and CSS measure angles in opposite directions, so we have to
1364 // invert the sign of the CSS oblique value when setting OpenType 'slnt'.
1365 aResult.AppendElement(gfxFontVariation{HB_TAG('s', 'l', 'n', 't'), -angle});
1366 }
1367
1368 auto replaceOrAppend = [&aResult](const gfxFontVariation& aSetting) {
1369 struct TagEquals {
1370 bool Equals(const gfxFontVariation& aIter, uint32_t aTag) const {
1371 return aIter.mTag == aTag;
1372 }
1373 };
1374 auto index = aResult.IndexOf(aSetting.mTag, 0, TagEquals());
1375 if (index == aResult.NoIndex) {
1376 aResult.AppendElement(aSetting);
1377 } else {
1378 aResult[index].mValue = aSetting.mValue;
1379 }
1380 };
1381
1382 // The low-level font-variation-settings descriptor from @font-face,
1383 // if present, takes precedence over automatic variation settings
1384 // from high-level properties.
1385 for (const auto& v : mVariationSettings) {
1386 replaceOrAppend(v);
1387 }
1388
1389 // And the low-level font-variation-settings property takes precedence
1390 // over the descriptor.
1391 for (const auto& v : aStyle.variationSettings) {
1392 replaceOrAppend(v);
1393 }
1394 }
1395
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const1396 size_t gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(
1397 mozilla::MallocSizeOf aMallocSizeOf) const {
1398 size_t n = 0;
1399 if (mBlob) {
1400 n += aMallocSizeOf(mBlob);
1401 }
1402 if (mSharedBlobData) {
1403 n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
1404 }
1405 return n;
1406 }
1407
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const1408 void gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1409 FontListSizes* aSizes) const {
1410 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1411
1412 // cmaps are shared so only non-shared cmaps are included here
1413 if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
1414 aSizes->mCharMapsSize += mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
1415 }
1416 if (mFontTableCache) {
1417 aSizes->mFontTableCacheSize +=
1418 mFontTableCache->SizeOfIncludingThis(aMallocSizeOf);
1419 }
1420
1421 // If the font has UVS data, we count that as part of the character map.
1422 if (mUVSData) {
1423 aSizes->mCharMapsSize += aMallocSizeOf(mUVSData.get());
1424 }
1425
1426 // The following, if present, are essentially cached forms of font table
1427 // data, so we'll accumulate them together with the basic table cache.
1428 if (mUserFontData) {
1429 aSizes->mFontTableCacheSize +=
1430 mUserFontData->SizeOfIncludingThis(aMallocSizeOf);
1431 }
1432 if (mSVGGlyphs) {
1433 aSizes->mFontTableCacheSize +=
1434 mSVGGlyphs->SizeOfIncludingThis(aMallocSizeOf);
1435 }
1436 if (mSupportedFeatures) {
1437 aSizes->mFontTableCacheSize +=
1438 mSupportedFeatures->ShallowSizeOfIncludingThis(aMallocSizeOf);
1439 }
1440 if (mFeatureInputs) {
1441 aSizes->mFontTableCacheSize +=
1442 mFeatureInputs->ShallowSizeOfIncludingThis(aMallocSizeOf);
1443 for (auto iter = mFeatureInputs->ConstIter(); !iter.Done(); iter.Next()) {
1444 // There's no API to get the real size of an hb_set, so we'll use
1445 // an approximation based on knowledge of the implementation.
1446 aSizes->mFontTableCacheSize += 8192; // vector of 64K bits
1447 }
1448 }
1449 // We don't include the size of mCOLR/mCPAL here, because (depending on the
1450 // font backend implementation) they will either wrap blocks of data owned
1451 // by the system (and potentially shared), or tables that are in our font
1452 // table cache and therefore already counted.
1453 }
1454
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const1455 void gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1456 FontListSizes* aSizes) const {
1457 aSizes->mFontListSize += aMallocSizeOf(this);
1458 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1459 }
1460
1461 // This is used to report the size of an individual downloaded font in the
1462 // user font cache. (Fonts that are part of the platform font list accumulate
1463 // their sizes to the font list's reporter using the AddSizeOf... methods
1464 // above.)
ComputedSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const1465 size_t gfxFontEntry::ComputedSizeOfExcludingThis(
1466 MallocSizeOf aMallocSizeOf) const {
1467 FontListSizes s = {0};
1468 AddSizeOfExcludingThis(aMallocSizeOf, &s);
1469
1470 // When reporting memory used for the main platform font list,
1471 // where we're typically summing the totals for a few hundred font faces,
1472 // we report the fields of FontListSizes separately.
1473 // But for downloaded user fonts, the actual resource data (added below)
1474 // will dominate, and the minor overhead of these pieces isn't worth
1475 // splitting out for an individual font.
1476 size_t result = s.mFontListSize + s.mFontTableCacheSize + s.mCharMapsSize;
1477
1478 if (mIsDataUserFont) {
1479 MOZ_ASSERT(mComputedSizeOfUserFont > 0, "user font with no data?");
1480 result += mComputedSizeOfUserFont;
1481 }
1482
1483 return result;
1484 }
1485
1486 //////////////////////////////////////////////////////////////////////////////
1487 //
1488 // class gfxFontFamily
1489 //
1490 //////////////////////////////////////////////////////////////////////////////
1491
1492 // we consider faces with mStandardFace == true to be "less than" those with
1493 // false, because during style matching, earlier entries are tried first
1494 class FontEntryStandardFaceComparator {
1495 public:
Equals(const RefPtr<gfxFontEntry> & a,const RefPtr<gfxFontEntry> & b) const1496 bool Equals(const RefPtr<gfxFontEntry>& a,
1497 const RefPtr<gfxFontEntry>& b) const {
1498 return a->mStandardFace == b->mStandardFace;
1499 }
LessThan(const RefPtr<gfxFontEntry> & a,const RefPtr<gfxFontEntry> & b) const1500 bool LessThan(const RefPtr<gfxFontEntry>& a,
1501 const RefPtr<gfxFontEntry>& b) const {
1502 return (a->mStandardFace == true && b->mStandardFace == false);
1503 }
1504 };
1505
SortAvailableFonts()1506 void gfxFontFamily::SortAvailableFonts() {
1507 mAvailableFonts.Sort(FontEntryStandardFaceComparator());
1508 }
1509
HasOtherFamilyNames()1510 bool gfxFontFamily::HasOtherFamilyNames() {
1511 // need to read in other family names to determine this
1512 if (!mOtherFamilyNamesInitialized) {
1513 ReadOtherFamilyNames(
1514 gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
1515 }
1516 return mHasOtherFamilyNames;
1517 }
1518
FindFontForStyle(const gfxFontStyle & aFontStyle,bool aIgnoreSizeTolerance)1519 gfxFontEntry* gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
1520 bool aIgnoreSizeTolerance) {
1521 AutoTArray<gfxFontEntry*, 4> matched;
1522 FindAllFontsForStyle(aFontStyle, matched, aIgnoreSizeTolerance);
1523 if (!matched.IsEmpty()) {
1524 return matched[0];
1525 }
1526 return nullptr;
1527 }
1528
WeightStyleStretchDistance(gfxFontEntry * aFontEntry,const gfxFontStyle & aTargetStyle)1529 static inline double WeightStyleStretchDistance(
1530 gfxFontEntry* aFontEntry, const gfxFontStyle& aTargetStyle) {
1531 double stretchDist =
1532 StretchDistance(aFontEntry->Stretch(), aTargetStyle.stretch);
1533 double styleDist =
1534 StyleDistance(aFontEntry->SlantStyle(), aTargetStyle.style);
1535 double weightDist = WeightDistance(aFontEntry->Weight(), aTargetStyle.weight);
1536
1537 // Sanity-check that the distances are within the expected range
1538 // (update if implementation of the distance functions is changed).
1539 MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
1540 MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
1541 MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
1542
1543 // weight/style/stretch priority: stretch >> style >> weight
1544 // so we multiply the stretch and style values to make them dominate
1545 // the result
1546 return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist;
1547 }
1548
FindAllFontsForStyle(const gfxFontStyle & aFontStyle,nsTArray<gfxFontEntry * > & aFontEntryList,bool aIgnoreSizeTolerance)1549 void gfxFontFamily::FindAllFontsForStyle(
1550 const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
1551 bool aIgnoreSizeTolerance) {
1552 if (!mHasStyles) {
1553 FindStyleVariations(); // collect faces for the family, if not already done
1554 }
1555
1556 NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
1557 NS_ASSERTION(aFontEntryList.IsEmpty(), "non-empty fontlist passed in");
1558
1559 gfxFontEntry* fe = nullptr;
1560
1561 // If the family has only one face, we simply return it; no further
1562 // checking needed
1563 uint32_t count = mAvailableFonts.Length();
1564 if (count == 1) {
1565 fe = mAvailableFonts[0];
1566 aFontEntryList.AppendElement(fe);
1567 return;
1568 }
1569
1570 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
1571 // or some subset of these. In this case, we have exactly 4 entries in
1572 // mAvailableFonts, stored in the above order; note that some of the entries
1573 // may be nullptr. We can then pick the required entry based on whether the
1574 // request is for bold or non-bold, italic or non-italic, without running the
1575 // more complex matching algorithm used for larger families with many weights
1576 // and/or widths.
1577
1578 if (mIsSimpleFamily) {
1579 // Family has no more than the "standard" 4 faces, at fixed indexes;
1580 // calculate which one we want.
1581 // Note that we cannot simply return it as not all 4 faces are necessarily
1582 // present.
1583 bool wantBold = aFontStyle.weight >= FontWeight(600);
1584 bool wantItalic = !aFontStyle.style.IsNormal();
1585 uint8_t faceIndex =
1586 (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
1587
1588 // if the desired style is available, return it directly
1589 fe = mAvailableFonts[faceIndex];
1590 if (fe) {
1591 aFontEntryList.AppendElement(fe);
1592 return;
1593 }
1594
1595 // order to check fallback faces in a simple family, depending on requested
1596 // style
1597 static const uint8_t simpleFallbacks[4][3] = {
1598 {kBoldFaceIndex, kItalicFaceIndex,
1599 kBoldItalicFaceIndex}, // fallbacks for Regular
1600 {kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex}, // Bold
1601 {kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex}, // Italic
1602 {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex} // BoldItalic
1603 };
1604 const uint8_t* order = simpleFallbacks[faceIndex];
1605
1606 for (uint8_t trial = 0; trial < 3; ++trial) {
1607 // check remaining faces in order of preference to find the first that
1608 // actually exists
1609 fe = mAvailableFonts[order[trial]];
1610 if (fe) {
1611 aFontEntryList.AppendElement(fe);
1612 return;
1613 }
1614 }
1615
1616 // this can't happen unless we have totally broken the font-list manager!
1617 MOZ_ASSERT_UNREACHABLE("no face found in simple font family!");
1618 }
1619
1620 // Pick the font(s) that are closest to the desired weight, style, and
1621 // stretch. Iterate over all fonts, measuring the weight/style distance.
1622 // Because of unicode-range values, there may be more than one font for a
1623 // given but the 99% use case is only a single font entry per
1624 // weight/style/stretch distance value. To optimize this, only add entries
1625 // to the matched font array when another entry already has the same
1626 // weight/style/stretch distance and add the last matched font entry. For
1627 // normal platform fonts with a single font entry for each
1628 // weight/style/stretch combination, only the last matched font entry will
1629 // be added.
1630
1631 double minDistance = INFINITY;
1632 gfxFontEntry* matched = nullptr;
1633 // iterate in forward order so that faces like 'Bold' are matched before
1634 // matching style distance faces such as 'Bold Outline' (see bug 1185812)
1635 for (uint32_t i = 0; i < count; i++) {
1636 fe = mAvailableFonts[i];
1637 // weight/style/stretch priority: stretch >> style >> weight
1638 double distance = WeightStyleStretchDistance(fe, aFontStyle);
1639 if (distance < minDistance) {
1640 matched = fe;
1641 if (!aFontEntryList.IsEmpty()) {
1642 aFontEntryList.Clear();
1643 }
1644 minDistance = distance;
1645 } else if (distance == minDistance) {
1646 if (matched) {
1647 aFontEntryList.AppendElement(matched);
1648 }
1649 matched = fe;
1650 }
1651 }
1652
1653 NS_ASSERTION(matched, "didn't match a font within a family");
1654
1655 if (matched) {
1656 aFontEntryList.AppendElement(matched);
1657 }
1658 }
1659
CheckForSimpleFamily()1660 void gfxFontFamily::CheckForSimpleFamily() {
1661 // already checked this family
1662 if (mIsSimpleFamily) {
1663 return;
1664 }
1665
1666 uint32_t count = mAvailableFonts.Length();
1667 if (count > 4 || count == 0) {
1668 return; // can't be "simple" if there are >4 faces;
1669 // if none then the family is unusable anyway
1670 }
1671
1672 if (count == 1) {
1673 mIsSimpleFamily = true;
1674 return;
1675 }
1676
1677 StretchRange firstStretch = mAvailableFonts[0]->Stretch();
1678 if (!firstStretch.IsSingle()) {
1679 return; // family with variation fonts is not considered "simple"
1680 }
1681
1682 gfxFontEntry* faces[4] = {0};
1683 for (uint8_t i = 0; i < count; ++i) {
1684 gfxFontEntry* fe = mAvailableFonts[i];
1685 if (fe->Stretch() != firstStretch || fe->IsOblique()) {
1686 // simple families don't have varying font-stretch or oblique
1687 return;
1688 }
1689 if (!fe->Weight().IsSingle() || !fe->SlantStyle().IsSingle()) {
1690 return; // family with variation fonts is not considered "simple"
1691 }
1692 uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
1693 (fe->SupportsBold() ? kBoldMask : 0);
1694 if (faces[faceIndex]) {
1695 return; // two faces resolve to the same slot; family isn't "simple"
1696 }
1697 faces[faceIndex] = fe;
1698 }
1699
1700 // we have successfully slotted the available faces into the standard
1701 // 4-face framework
1702 mAvailableFonts.SetLength(4);
1703 for (uint8_t i = 0; i < 4; ++i) {
1704 if (mAvailableFonts[i].get() != faces[i]) {
1705 mAvailableFonts[i].swap(faces[i]);
1706 }
1707 }
1708
1709 mIsSimpleFamily = true;
1710 }
1711
1712 #ifdef DEBUG
ContainsFace(gfxFontEntry * aFontEntry)1713 bool gfxFontFamily::ContainsFace(gfxFontEntry* aFontEntry) {
1714 uint32_t i, numFonts = mAvailableFonts.Length();
1715 for (i = 0; i < numFonts; i++) {
1716 if (mAvailableFonts[i] == aFontEntry) {
1717 return true;
1718 }
1719 // userfonts contain the actual real font entry
1720 if (mAvailableFonts[i] && mAvailableFonts[i]->mIsUserFontContainer) {
1721 gfxUserFontEntry* ufe =
1722 static_cast<gfxUserFontEntry*>(mAvailableFonts[i].get());
1723 if (ufe->GetPlatformFontEntry() == aFontEntry) {
1724 return true;
1725 }
1726 }
1727 }
1728 return false;
1729 }
1730 #endif
1731
LocalizedName(nsACString & aLocalizedName)1732 void gfxFontFamily::LocalizedName(nsACString& aLocalizedName) {
1733 // just return the primary name; subclasses should override
1734 aLocalizedName = mName;
1735 }
1736
FindFontForChar(GlobalFontMatch * aMatchData)1737 void gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) {
1738 if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
1739 // none of the faces in the family support the required char,
1740 // so bail out immediately
1741 return;
1742 }
1743
1744 #ifdef MOZ_GECKO_PROFILER
1745 nsCString charAndName;
1746 if (profiler_can_accept_markers()) {
1747 charAndName = nsPrintfCString("\\u%x %s", aMatchData->mCh, mName.get());
1748 }
1749 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxFontFamily::FindFontForChar",
1750 LAYOUT, charAndName);
1751 #endif
1752
1753 AutoTArray<gfxFontEntry*, 4> entries;
1754 FindAllFontsForStyle(aMatchData->mStyle, entries,
1755 /*aIgnoreSizeTolerance*/ true);
1756 if (entries.IsEmpty()) {
1757 return;
1758 }
1759
1760 gfxFontEntry* fe = nullptr;
1761 float distance = INFINITY;
1762
1763 for (auto e : entries) {
1764 if (e->SkipDuringSystemFallback()) {
1765 continue;
1766 }
1767
1768 aMatchData->mCmapsTested++;
1769 if (e->HasCharacter(aMatchData->mCh)) {
1770 aMatchData->mCount++;
1771
1772 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
1773
1774 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
1775 Script script = GetScriptCode(aMatchData->mCh);
1776 MOZ_LOG(log, LogLevel::Debug,
1777 ("(textrun-systemfallback-fonts) char: u+%6.6x "
1778 "script: %d match: [%s]\n",
1779 aMatchData->mCh, int(script), e->Name().get()));
1780 }
1781
1782 fe = e;
1783 distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
1784 break;
1785 }
1786 }
1787
1788 if (!fe && !aMatchData->mStyle.IsNormalStyle()) {
1789 // If style/weight/stretch was not Normal, see if we can
1790 // fall back to a next-best face (e.g. Arial Black -> Bold,
1791 // or Arial Narrow -> Regular).
1792 GlobalFontMatch data(aMatchData->mCh, aMatchData->mStyle);
1793 SearchAllFontsForChar(&data);
1794 if (!data.mBestMatch) {
1795 return;
1796 }
1797 fe = data.mBestMatch;
1798 distance = data.mMatchDistance;
1799 }
1800
1801 if (!fe) {
1802 return;
1803 }
1804
1805 if (distance < aMatchData->mMatchDistance ||
1806 (distance == aMatchData->mMatchDistance &&
1807 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
1808 aMatchData->mBestMatch = fe;
1809 aMatchData->mMatchedFamily = this;
1810 aMatchData->mMatchDistance = distance;
1811 }
1812 }
1813
SearchAllFontsForChar(GlobalFontMatch * aMatchData)1814 void gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) {
1815 if (!mFamilyCharacterMapInitialized) {
1816 ReadAllCMAPs();
1817 }
1818 if (!mFamilyCharacterMap.test(aMatchData->mCh)) {
1819 return;
1820 }
1821 uint32_t i, numFonts = mAvailableFonts.Length();
1822 for (i = 0; i < numFonts; i++) {
1823 gfxFontEntry* fe = mAvailableFonts[i];
1824 if (fe && fe->HasCharacter(aMatchData->mCh)) {
1825 float distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
1826 if (distance < aMatchData->mMatchDistance ||
1827 (distance == aMatchData->mMatchDistance &&
1828 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
1829 aMatchData->mBestMatch = fe;
1830 aMatchData->mMatchedFamily = this;
1831 aMatchData->mMatchDistance = distance;
1832 }
1833 }
1834 }
1835 }
1836
1837 /*virtual*/
~gfxFontFamily()1838 gfxFontFamily::~gfxFontFamily() {
1839 // Should not be dropped by stylo
1840 MOZ_ASSERT(NS_IsMainThread());
1841 }
1842
1843 // returns true if other names were found, false otherwise
ReadOtherFamilyNamesForFace(gfxPlatformFontList * aPlatformFontList,hb_blob_t * aNameTable,bool useFullName)1844 bool gfxFontFamily::ReadOtherFamilyNamesForFace(
1845 gfxPlatformFontList* aPlatformFontList, hb_blob_t* aNameTable,
1846 bool useFullName) {
1847 uint32_t dataLength;
1848 const char* nameData = hb_blob_get_data(aNameTable, &dataLength);
1849 AutoTArray<nsCString, 4> otherFamilyNames;
1850
1851 gfxFontUtils::ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
1852 otherFamilyNames, useFullName);
1853
1854 uint32_t n = otherFamilyNames.Length();
1855 for (uint32_t i = 0; i < n; i++) {
1856 aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
1857 }
1858
1859 return n != 0;
1860 }
1861
ReadOtherFamilyNames(gfxPlatformFontList * aPlatformFontList)1862 void gfxFontFamily::ReadOtherFamilyNames(
1863 gfxPlatformFontList* aPlatformFontList) {
1864 if (mOtherFamilyNamesInitialized) return;
1865 mOtherFamilyNamesInitialized = true;
1866
1867 FindStyleVariations();
1868
1869 // read in other family names for the first face in the list
1870 uint32_t i, numFonts = mAvailableFonts.Length();
1871 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
1872
1873 for (i = 0; i < numFonts; ++i) {
1874 gfxFontEntry* fe = mAvailableFonts[i];
1875 if (!fe) {
1876 continue;
1877 }
1878 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1879 if (!nameTable) {
1880 continue;
1881 }
1882 mHasOtherFamilyNames =
1883 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1884 break;
1885 }
1886
1887 // read in other names for the first face in the list with the assumption
1888 // that if extra names don't exist in that face then they don't exist in
1889 // other faces for the same font
1890 if (!mHasOtherFamilyNames) return;
1891
1892 // read in names for all faces, needed to catch cases where fonts have
1893 // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
1894 for (; i < numFonts; i++) {
1895 gfxFontEntry* fe = mAvailableFonts[i];
1896 if (!fe) {
1897 continue;
1898 }
1899 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1900 if (!nameTable) {
1901 continue;
1902 }
1903 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1904 }
1905 }
1906
LookForLegacyFamilyName(const nsACString & aCanonicalName,const char * aNameData,uint32_t aDataLength,nsACString & aLegacyName)1907 static bool LookForLegacyFamilyName(const nsACString& aCanonicalName,
1908 const char* aNameData, uint32_t aDataLength,
1909 nsACString& aLegacyName /* outparam */) {
1910 const gfxFontUtils::NameHeader* nameHeader =
1911 reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
1912
1913 uint32_t nameCount = nameHeader->count;
1914 if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
1915 NS_WARNING("invalid font (name records)");
1916 return false;
1917 }
1918
1919 const gfxFontUtils::NameRecord* nameRecord =
1920 reinterpret_cast<const gfxFontUtils::NameRecord*>(
1921 aNameData + sizeof(gfxFontUtils::NameHeader));
1922 uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
1923
1924 for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
1925 uint32_t nameLen = nameRecord->length;
1926 uint32_t nameOff = nameRecord->offset;
1927
1928 if (stringsBase + nameOff + nameLen > aDataLength) {
1929 NS_WARNING("invalid font (name table strings)");
1930 return false;
1931 }
1932
1933 if (uint16_t(nameRecord->nameID) == gfxFontUtils::NAME_ID_FAMILY) {
1934 bool ok = gfxFontUtils::DecodeFontName(
1935 aNameData + stringsBase + nameOff, nameLen,
1936 uint32_t(nameRecord->platformID), uint32_t(nameRecord->encodingID),
1937 uint32_t(nameRecord->languageID), aLegacyName);
1938 // it's only a legacy name if it differs from the canonical name
1939 if (ok && aLegacyName != aCanonicalName) {
1940 return true;
1941 }
1942 }
1943 }
1944 return false;
1945 }
1946
CheckForLegacyFamilyNames(gfxPlatformFontList * aFontList)1947 bool gfxFontFamily::CheckForLegacyFamilyNames(gfxPlatformFontList* aFontList) {
1948 if (mCheckedForLegacyFamilyNames) {
1949 // we already did this, so there's nothing more to add
1950 return false;
1951 }
1952 mCheckedForLegacyFamilyNames = true;
1953 bool added = false;
1954 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
1955 // Make a local copy of the array of font faces, in case of changes
1956 // during the iteration.
1957 for (auto& fe :
1958 CopyableAutoTArray<RefPtr<gfxFontEntry>, 8>(mAvailableFonts)) {
1959 if (!fe) {
1960 continue;
1961 }
1962 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1963 if (!nameTable) {
1964 continue;
1965 }
1966 nsAutoCString legacyName;
1967 uint32_t dataLength;
1968 const char* nameData = hb_blob_get_data(nameTable, &dataLength);
1969 if (LookForLegacyFamilyName(Name(), nameData, dataLength, legacyName)) {
1970 if (aFontList->AddWithLegacyFamilyName(legacyName, fe, mVisibility)) {
1971 added = true;
1972 }
1973 }
1974 }
1975 return added;
1976 }
1977
ReadFaceNames(gfxPlatformFontList * aPlatformFontList,bool aNeedFullnamePostscriptNames,FontInfoData * aFontInfoData)1978 void gfxFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
1979 bool aNeedFullnamePostscriptNames,
1980 FontInfoData* aFontInfoData) {
1981 // if all needed names have already been read, skip
1982 if (mOtherFamilyNamesInitialized &&
1983 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
1984 return;
1985 }
1986
1987 bool asyncFontLoaderDisabled = false;
1988
1989 if (!mOtherFamilyNamesInitialized && aFontInfoData &&
1990 aFontInfoData->mLoadOtherNames && !asyncFontLoaderDisabled) {
1991 const auto* otherFamilyNames = aFontInfoData->GetOtherFamilyNames(mName);
1992 if (otherFamilyNames) {
1993 uint32_t i, n = otherFamilyNames->Length();
1994 for (i = 0; i < n; i++) {
1995 aPlatformFontList->AddOtherFamilyName(this, (*otherFamilyNames)[i]);
1996 }
1997 }
1998 mOtherFamilyNamesInitialized = true;
1999 }
2000
2001 // if all needed data has been initialized, return
2002 if (mOtherFamilyNamesInitialized &&
2003 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2004 return;
2005 }
2006
2007 FindStyleVariations(aFontInfoData);
2008
2009 // check again, as style enumeration code may have loaded names
2010 if (mOtherFamilyNamesInitialized &&
2011 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2012 return;
2013 }
2014
2015 uint32_t i, numFonts = mAvailableFonts.Length();
2016 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
2017
2018 bool firstTime = true, readAllFaces = false;
2019 for (i = 0; i < numFonts; ++i) {
2020 gfxFontEntry* fe = mAvailableFonts[i];
2021 if (!fe) {
2022 continue;
2023 }
2024
2025 nsAutoCString fullname, psname;
2026 bool foundFaceNames = false;
2027 if (!mFaceNamesInitialized && aNeedFullnamePostscriptNames &&
2028 aFontInfoData && aFontInfoData->mLoadFaceNames) {
2029 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
2030 if (!fullname.IsEmpty()) {
2031 aPlatformFontList->AddFullname(fe, fullname);
2032 }
2033 if (!psname.IsEmpty()) {
2034 aPlatformFontList->AddPostscriptName(fe, psname);
2035 }
2036 foundFaceNames = true;
2037
2038 // found everything needed? skip to next font
2039 if (mOtherFamilyNamesInitialized) {
2040 continue;
2041 }
2042 }
2043
2044 // load directly from the name table
2045 gfxFontEntry::AutoTable nameTable(fe, kNAME);
2046 if (!nameTable) {
2047 continue;
2048 }
2049
2050 if (aNeedFullnamePostscriptNames && !foundFaceNames) {
2051 if (gfxFontUtils::ReadCanonicalName(nameTable, gfxFontUtils::NAME_ID_FULL,
2052 fullname) == NS_OK) {
2053 aPlatformFontList->AddFullname(fe, fullname);
2054 }
2055
2056 if (gfxFontUtils::ReadCanonicalName(
2057 nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK) {
2058 aPlatformFontList->AddPostscriptName(fe, psname);
2059 }
2060 }
2061
2062 if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
2063 bool foundOtherName =
2064 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
2065
2066 // if the first face has a different name, scan all faces, otherwise
2067 // assume the family doesn't have other names
2068 if (firstTime && foundOtherName) {
2069 mHasOtherFamilyNames = true;
2070 readAllFaces = true;
2071 }
2072 firstTime = false;
2073 }
2074
2075 // if not reading in any more names, skip other faces
2076 if (!readAllFaces && !aNeedFullnamePostscriptNames) {
2077 break;
2078 }
2079 }
2080
2081 mFaceNamesInitialized = true;
2082 mOtherFamilyNamesInitialized = true;
2083 }
2084
FindFont(const nsACString & aPostscriptName)2085 gfxFontEntry* gfxFontFamily::FindFont(const nsACString& aPostscriptName) {
2086 // find the font using a simple linear search
2087 uint32_t numFonts = mAvailableFonts.Length();
2088 for (uint32_t i = 0; i < numFonts; i++) {
2089 gfxFontEntry* fe = mAvailableFonts[i].get();
2090 if (fe && fe->Name() == aPostscriptName) return fe;
2091 }
2092 return nullptr;
2093 }
2094
ReadAllCMAPs(FontInfoData * aFontInfoData)2095 void gfxFontFamily::ReadAllCMAPs(FontInfoData* aFontInfoData) {
2096 FindStyleVariations(aFontInfoData);
2097
2098 uint32_t i, numFonts = mAvailableFonts.Length();
2099 for (i = 0; i < numFonts; i++) {
2100 gfxFontEntry* fe = mAvailableFonts[i];
2101 // don't try to load cmaps for downloadable fonts not yet loaded
2102 if (!fe || fe->mIsUserFontContainer) {
2103 continue;
2104 }
2105 fe->ReadCMAP(aFontInfoData);
2106 mFamilyCharacterMap.Union(*(fe->mCharacterMap));
2107 }
2108 mFamilyCharacterMap.Compact();
2109 mFamilyCharacterMapInitialized = true;
2110 }
2111
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const2112 void gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2113 FontListSizes* aSizes) const {
2114 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2115 aSizes->mCharMapsSize +=
2116 mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
2117
2118 aSizes->mFontListSize +=
2119 mAvailableFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
2120 for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
2121 gfxFontEntry* fe = mAvailableFonts[i];
2122 if (fe) {
2123 fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2124 }
2125 }
2126 }
2127
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const2128 void gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2129 FontListSizes* aSizes) const {
2130 aSizes->mFontListSize += aMallocSizeOf(this);
2131 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2132 }
2133