Bug 1876318 - set shipping-product for push-bundle tasks. r=bhearsum,releng-reviewers
[gecko.git] / gfx / thebes / gfxFontEntry.cpp
bloba9fe04125c0768d9090d871c46494443e2298e2c
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/. */
6 #include "gfxFontEntry.h"
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/FontPropertyTypes.h"
10 #include "mozilla/MathAlgorithms.h"
12 #include "mozilla/Logging.h"
14 #include "gfxTextRun.h"
15 #include "gfxPlatform.h"
16 #include "nsGkAtoms.h"
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 #include "mozilla/Likely.h"
32 #include "mozilla/MemoryReporting.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/ProfilerLabels.h"
35 #include "mozilla/ScopeExit.h"
36 #include "mozilla/Services.h"
37 #include "mozilla/StaticPrefs_layout.h"
38 #include "mozilla/Telemetry.h"
39 #include "gfxSVGGlyphs.h"
40 #include "gfx2DGlue.h"
42 #include "harfbuzz/hb.h"
43 #include "harfbuzz/hb-ot.h"
44 #include "graphite2/Font.h"
46 #include "ThebesRLBox.h"
48 #include <algorithm>
50 using namespace mozilla;
51 using namespace mozilla::gfx;
52 using namespace mozilla::unicode;
54 void gfxCharacterMap::NotifyMaybeReleased(gfxCharacterMap* aCmap) {
55 // Tell gfxPlatformFontList that a charmap's refcount was decremented,
56 // so it should check whether the object is to be deleted.
57 gfxPlatformFontList::PlatformFontList()->MaybeRemoveCmap(aCmap);
60 gfxFontEntry::gfxFontEntry(const nsACString& aName, bool aIsStandardFace)
61 : mName(aName),
62 mLock("gfxFontEntry lock"),
63 mFeatureInfoLock("gfxFontEntry featureInfo mutex"),
64 mFixedPitch(false),
65 mIsBadUnderlineFont(false),
66 mIsUserFontContainer(false),
67 mIsDataUserFont(false),
68 mIsLocalUserFont(false),
69 mStandardFace(aIsStandardFace),
70 mIgnoreGDEF(false),
71 mIgnoreGSUB(false),
72 mSkipDefaultFeatureSpaceCheck(false),
73 mSVGInitialized(false),
74 mHasCmapTable(false),
75 mGrFaceInitialized(false),
76 mCheckedForColorGlyph(false),
77 mCheckedForVariationAxes(false),
78 mSpaceGlyphIsInvisible(LazyFlag::Uninitialized),
79 mHasGraphiteTables(LazyFlag::Uninitialized),
80 mHasGraphiteSpaceContextuals(LazyFlag::Uninitialized),
81 mHasColorBitmapTable(LazyFlag::Uninitialized),
82 mHasSpaceFeatures(SpaceFeatures::Uninitialized) {
83 mTrakTable.exchange(kTrakTableUninitialized);
84 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
85 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
88 gfxFontEntry::~gfxFontEntry() {
89 // Should not be dropped by stylo
90 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
92 hb_blob_destroy(mCOLR.exchange(nullptr));
93 hb_blob_destroy(mCPAL.exchange(nullptr));
95 if (TrakTableInitialized()) {
96 // Only if it was initialized, so that we don't try to call hb_blob_destroy
97 // on the kTrakTableUninitialized flag value!
98 hb_blob_destroy(mTrakTable.exchange(nullptr));
101 // For downloaded fonts, we need to tell the user font cache that this
102 // entry is being deleted.
103 if (mIsDataUserFont) {
104 gfxUserFontSet::UserFontCache::ForgetFont(this);
107 if (mFeatureInputs) {
108 for (auto iter = mFeatureInputs->Iter(); !iter.Done(); iter.Next()) {
109 hb_set_t*& set = iter.Data();
110 hb_set_destroy(set);
114 delete mFontTableCache.exchange(nullptr);
115 delete mSVGGlyphs.exchange(nullptr);
116 delete[] mUVSData.exchange(nullptr);
118 gfxCharacterMap* cmap = mCharacterMap.exchange(nullptr);
119 NS_IF_RELEASE(cmap);
121 // By the time the entry is destroyed, all font instances that were
122 // using it should already have been deleted, and so the HB and/or Gr
123 // face objects should have been released.
124 MOZ_ASSERT(!mHBFace);
125 MOZ_ASSERT(!mGrFaceInitialized);
128 // Only used during initialization, before any other thread has a chance to see
129 // the entry, so locking not required.
130 void gfxFontEntry::InitializeFrom(fontlist::Face* aFace,
131 const fontlist::Family* aFamily) {
132 mShmemFace = aFace;
133 mShmemFamily = aFamily;
134 mStyleRange = aFace->mStyle;
135 mWeightRange = aFace->mWeight;
136 mStretchRange = aFace->mStretch;
137 mFixedPitch = aFace->mFixedPitch;
138 mIsBadUnderlineFont = aFamily->IsBadUnderlineFamily();
139 auto* list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
140 mFamilyName = aFamily->DisplayName().AsString(list);
141 mHasCmapTable = TrySetShmemCharacterMap();
144 bool gfxFontEntry::TrySetShmemCharacterMap() {
145 MOZ_ASSERT(mShmemFace);
146 auto list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
147 auto* shmemCmap = mShmemFace->mCharacterMap.ToPtr<const SharedBitSet>(list);
148 mShmemCharacterMap.exchange(shmemCmap);
149 return shmemCmap != nullptr;
152 bool gfxFontEntry::TestCharacterMap(uint32_t aCh) {
153 if (!mCharacterMap && !mShmemCharacterMap) {
154 ReadCMAP();
155 MOZ_ASSERT(mCharacterMap || mShmemCharacterMap,
156 "failed to initialize character map");
158 return mShmemCharacterMap ? GetShmemCharacterMap()->test(aCh)
159 : GetCharacterMap()->test(aCh);
162 void gfxFontEntry::EnsureUVSMapInitialized() {
163 // mUVSOffset will not be initialized
164 // until cmap is initialized.
165 if (!mCharacterMap && !mShmemCharacterMap) {
166 ReadCMAP();
167 NS_ASSERTION(mCharacterMap || mShmemCharacterMap,
168 "failed to initialize character map");
171 if (!mUVSOffset) {
172 return;
175 if (!mUVSData) {
176 nsresult rv = NS_ERROR_NOT_AVAILABLE;
177 const uint32_t kCmapTag = TRUETYPE_TAG('c', 'm', 'a', 'p');
178 AutoTable cmapTable(this, kCmapTag);
179 if (cmapTable) {
180 const uint8_t* uvsData = nullptr;
181 unsigned int cmapLen;
182 const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
183 rv = gfxFontUtils::ReadCMAPTableFormat14(
184 (const uint8_t*)cmapData + mUVSOffset, cmapLen - mUVSOffset, uvsData);
185 if (NS_SUCCEEDED(rv)) {
186 if (!mUVSData.compareExchange(nullptr, uvsData)) {
187 delete uvsData;
191 if (NS_FAILED(rv)) {
192 mUVSOffset = 0; // don't try to read the table again
197 uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS) {
198 EnsureUVSMapInitialized();
200 if (const auto* uvsData = GetUVSData()) {
201 return gfxFontUtils::MapUVSToGlyphFormat14(uvsData, aCh, aVS);
204 return 0;
207 bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags,
208 uint32_t aNumTags) {
209 auto face(GetHBFace());
211 unsigned int index;
212 hb_tag_t chosenScript;
213 bool found = hb_ot_layout_table_select_script(
214 face, TRUETYPE_TAG('G', 'S', 'U', 'B'), aNumTags, aScriptTags, &index,
215 &chosenScript);
217 return found && chosenScript != TRUETYPE_TAG('D', 'F', 'L', 'T');
220 nsresult gfxFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
221 MOZ_ASSERT(false, "using default no-op implementation of ReadCMAP");
222 RefPtr<gfxCharacterMap> cmap = new gfxCharacterMap();
223 if (mCharacterMap.compareExchange(nullptr, cmap.get())) {
224 Unused << cmap.forget(); // mCharacterMap now owns the reference
226 return NS_OK;
229 nsCString gfxFontEntry::RealFaceName() {
230 AutoTable nameTable(this, TRUETYPE_TAG('n', 'a', 'm', 'e'));
231 if (nameTable) {
232 nsAutoCString name;
233 nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
234 if (NS_SUCCEEDED(rv)) {
235 return std::move(name);
238 return Name();
241 already_AddRefed<gfxFont> gfxFontEntry::FindOrMakeFont(
242 const gfxFontStyle* aStyle, gfxCharacterMap* aUnicodeRangeMap) {
243 RefPtr<gfxFont> font =
244 gfxFontCache::GetCache()->Lookup(this, aStyle, aUnicodeRangeMap);
245 if (font) {
246 return font.forget();
249 gfxFont* newFont = CreateFontInstance(aStyle);
250 if (!newFont) {
251 return nullptr;
253 if (!newFont->Valid()) {
254 newFont->Destroy();
255 return nullptr;
257 newFont->SetUnicodeRangeMap(aUnicodeRangeMap);
258 return gfxFontCache::GetCache()->MaybeInsert(newFont);
261 uint16_t gfxFontEntry::UnitsPerEm() {
262 if (!mUnitsPerEm) {
263 AutoTable headTable(this, TRUETYPE_TAG('h', 'e', 'a', 'd'));
264 if (headTable) {
265 uint32_t len;
266 const HeadTable* head =
267 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, &len));
268 if (len >= sizeof(HeadTable)) {
269 mUnitsPerEm = head->unitsPerEm;
270 if (int16_t(head->xMax) > int16_t(head->xMin) &&
271 int16_t(head->yMax) > int16_t(head->yMin)) {
272 mXMin = head->xMin;
273 mYMin = head->yMin;
274 mXMax = head->xMax;
275 mYMax = head->yMax;
280 // if we didn't find a usable 'head' table, or if the value was
281 // outside the valid range, record it as invalid
282 if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
283 mUnitsPerEm = kInvalidUPEM;
286 return mUnitsPerEm;
289 bool gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId) {
290 NS_ASSERTION(mSVGInitialized,
291 "SVG data has not yet been loaded. TryGetSVGData() first.");
292 return GetSVGGlyphs()->HasSVGGlyph(aGlyphId);
295 bool gfxFontEntry::GetSVGGlyphExtents(DrawTarget* aDrawTarget,
296 uint32_t aGlyphId, gfxFloat aSize,
297 gfxRect* aResult) {
298 MOZ_ASSERT(mSVGInitialized,
299 "SVG data has not yet been loaded. TryGetSVGData() first.");
300 MOZ_ASSERT(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
301 "font has invalid unitsPerEm");
303 gfxMatrix svgToApp(aSize / mUnitsPerEm, 0, 0, aSize / mUnitsPerEm, 0, 0);
304 return GetSVGGlyphs()->GetGlyphExtents(aGlyphId, svgToApp, aResult);
307 void gfxFontEntry::RenderSVGGlyph(gfxContext* aContext, uint32_t aGlyphId,
308 SVGContextPaint* aContextPaint) {
309 NS_ASSERTION(mSVGInitialized,
310 "SVG data has not yet been loaded. TryGetSVGData() first.");
311 GetSVGGlyphs()->RenderGlyph(aContext, aGlyphId, aContextPaint);
314 bool gfxFontEntry::TryGetSVGData(const gfxFont* aFont) {
315 if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
316 return false;
319 // We don't support SVG-in-OT glyphs in offscreen-canvas worker threads.
320 if (!NS_IsMainThread()) {
321 return false;
324 if (!mSVGInitialized) {
325 // If UnitsPerEm is not known/valid, we can't use SVG glyphs
326 if (UnitsPerEm() == kInvalidUPEM) {
327 mSVGInitialized = true;
328 return false;
331 // We don't use AutoTable here because we'll pass ownership of this
332 // blob to the gfxSVGGlyphs, once we've confirmed the table exists
333 hb_blob_t* svgTable = GetFontTable(TRUETYPE_TAG('S', 'V', 'G', ' '));
334 if (!svgTable) {
335 mSVGInitialized = true;
336 return false;
339 // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
340 // with it.
341 auto* svgGlyphs = new gfxSVGGlyphs(svgTable, this);
342 if (!mSVGGlyphs.compareExchange(nullptr, svgGlyphs)) {
343 delete svgGlyphs;
345 mSVGInitialized = true;
348 if (GetSVGGlyphs()) {
349 AutoWriteLock lock(mLock);
350 if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
351 mFontsUsingSVGGlyphs.AppendElement(aFont);
355 return !!GetSVGGlyphs();
358 void gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont) {
359 AutoWriteLock lock(mLock);
360 mFontsUsingSVGGlyphs.RemoveElement(aFont);
363 void gfxFontEntry::NotifyGlyphsChanged() {
364 AutoReadLock lock(mLock);
365 for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
366 const gfxFont* font = mFontsUsingSVGGlyphs[i];
367 font->NotifyGlyphsChanged();
371 bool gfxFontEntry::TryGetColorGlyphs() {
372 if (mCheckedForColorGlyph) {
373 return mCOLR && mCPAL;
376 auto* colr = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
377 auto* cpal = colr ? GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L')) : nullptr;
379 if (colr && cpal && gfx::COLRFonts::ValidateColorGlyphs(colr, cpal)) {
380 if (!mCOLR.compareExchange(nullptr, colr)) {
381 hb_blob_destroy(colr);
383 if (!mCPAL.compareExchange(nullptr, cpal)) {
384 hb_blob_destroy(cpal);
386 } else {
387 hb_blob_destroy(colr);
388 hb_blob_destroy(cpal);
391 mCheckedForColorGlyph = true;
392 return mCOLR && mCPAL;
396 * FontTableBlobData
398 * See FontTableHashEntry for the general strategy.
401 class gfxFontEntry::FontTableBlobData {
402 public:
403 explicit FontTableBlobData(nsTArray<uint8_t>&& aBuffer)
404 : mTableData(std::move(aBuffer)), mHashtable(nullptr), mHashKey(0) {
405 MOZ_COUNT_CTOR(FontTableBlobData);
408 ~FontTableBlobData() {
409 MOZ_COUNT_DTOR(FontTableBlobData);
410 if (mHashtable && mHashKey) {
411 mHashtable->RemoveEntry(mHashKey);
415 // Useful for creating blobs
416 const char* GetTable() const {
417 return reinterpret_cast<const char*>(mTableData.Elements());
419 uint32_t GetTableLength() const { return mTableData.Length(); }
421 // Tell this FontTableBlobData to remove the HashEntry when this is
422 // destroyed.
423 void ManageHashEntry(nsTHashtable<FontTableHashEntry>* aHashtable,
424 uint32_t aHashKey) {
425 mHashtable = aHashtable;
426 mHashKey = aHashKey;
429 // Disconnect from the HashEntry (because the blob has already been
430 // removed from the hashtable).
431 void ForgetHashEntry() {
432 mHashtable = nullptr;
433 mHashKey = 0;
436 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
437 return mTableData.ShallowSizeOfExcludingThis(aMallocSizeOf);
439 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
440 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
443 private:
444 // The font table data block
445 nsTArray<uint8_t> mTableData;
447 // The blob destroy function needs to know the owning hashtable
448 // and the hashtable key, so that it can remove the entry.
449 nsTHashtable<FontTableHashEntry>* mHashtable;
450 uint32_t mHashKey;
452 // not implemented
453 FontTableBlobData(const FontTableBlobData&);
456 hb_blob_t* gfxFontEntry::FontTableHashEntry::ShareTableAndGetBlob(
457 nsTArray<uint8_t>&& aTable, nsTHashtable<FontTableHashEntry>* aHashtable) {
458 Clear();
459 // adopts elements of aTable
460 mSharedBlobData = new FontTableBlobData(std::move(aTable));
462 mBlob = hb_blob_create(
463 mSharedBlobData->GetTable(), mSharedBlobData->GetTableLength(),
464 HB_MEMORY_MODE_READONLY, mSharedBlobData, DeleteFontTableBlobData);
465 if (mBlob == hb_blob_get_empty()) {
466 // The FontTableBlobData was destroyed during hb_blob_create().
467 // The (empty) blob is still be held in the hashtable with a strong
468 // reference.
469 return hb_blob_reference(mBlob);
472 // Tell the FontTableBlobData to remove this hash entry when destroyed.
473 // The hashtable does not keep a strong reference.
474 mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
475 return mBlob;
478 void gfxFontEntry::FontTableHashEntry::Clear() {
479 // If the FontTableBlobData is managing the hash entry, then the blob is
480 // not owned by this HashEntry; otherwise there is strong reference to the
481 // blob that must be removed.
482 if (mSharedBlobData) {
483 mSharedBlobData->ForgetHashEntry();
484 mSharedBlobData = nullptr;
485 } else {
486 hb_blob_destroy(mBlob);
488 mBlob = nullptr;
491 // a hb_destroy_func for hb_blob_create
493 /* static */
494 void gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(
495 void* aBlobData) {
496 delete static_cast<FontTableBlobData*>(aBlobData);
499 hb_blob_t* gfxFontEntry::FontTableHashEntry::GetBlob() const {
500 return hb_blob_reference(mBlob);
503 bool gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob) {
504 // Accessing the mFontTableCache pointer is atomic, so we don't need to take
505 // a write lock even if we're initializing it here...
506 MOZ_PUSH_IGNORE_THREAD_SAFETY
507 if (MOZ_UNLIKELY(!mFontTableCache)) {
508 // We do this here rather than on fontEntry construction
509 // because not all shapers will access the table cache at all.
511 // We're not holding a write lock, so make sure to atomically update
512 // the cache pointer.
513 auto* newCache = new FontTableCache(8);
514 if (MOZ_UNLIKELY(!mFontTableCache.compareExchange(nullptr, newCache))) {
515 delete newCache;
518 FontTableCache* cache = GetFontTableCache();
519 MOZ_POP_THREAD_SAFETY
521 // ...but we do need a lock to read the actual hashtable contents.
522 AutoReadLock lock(mLock);
523 FontTableHashEntry* entry = cache->GetEntry(aTag);
524 if (!entry) {
525 return false;
528 *aBlob = entry->GetBlob();
529 return true;
532 hb_blob_t* gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
533 nsTArray<uint8_t>* aBuffer) {
534 MOZ_PUSH_IGNORE_THREAD_SAFETY
535 if (MOZ_UNLIKELY(!mFontTableCache)) {
536 auto* newCache = new FontTableCache(8);
537 if (MOZ_UNLIKELY(!mFontTableCache.compareExchange(nullptr, newCache))) {
538 delete newCache;
541 FontTableCache* cache = GetFontTableCache();
542 MOZ_POP_THREAD_SAFETY
544 AutoWriteLock lock(mLock);
545 FontTableHashEntry* entry = cache->PutEntry(aTag);
546 if (MOZ_UNLIKELY(!entry)) { // OOM
547 return nullptr;
550 if (!aBuffer) {
551 // ensure the entry is null
552 entry->Clear();
553 return nullptr;
556 return entry->ShareTableAndGetBlob(std::move(*aBuffer), cache);
559 already_AddRefed<gfxCharacterMap> gfxFontEntry::GetCMAPFromFontInfo(
560 FontInfoData* aFontInfoData, uint32_t& aUVSOffset) {
561 if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
562 return nullptr;
565 return aFontInfoData->GetCMAP(mName, aUVSOffset);
568 hb_blob_t* gfxFontEntry::GetFontTable(uint32_t aTag) {
569 hb_blob_t* blob;
570 if (GetExistingFontTable(aTag, &blob)) {
571 return blob;
574 nsTArray<uint8_t> buffer;
575 bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
577 return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
580 // callback for HarfBuzz to get a font table (in hb_blob_t form)
581 // from the font entry (passed as aUserData)
582 /*static*/
583 hb_blob_t* gfxFontEntry::HBGetTable(hb_face_t* face, uint32_t aTag,
584 void* aUserData) {
585 gfxFontEntry* fontEntry = static_cast<gfxFontEntry*>(aUserData);
587 // bug 589682 - ignore the GDEF table in buggy fonts (applies to
588 // Italic and BoldItalic faces of Times New Roman)
589 if (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') && fontEntry->IgnoreGDEF()) {
590 return nullptr;
593 // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
594 // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
595 if (aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') && fontEntry->IgnoreGSUB()) {
596 return nullptr;
599 return fontEntry->GetFontTable(aTag);
602 static thread_local gfxFontEntry* tl_grGetFontTableCallbackData = nullptr;
604 class gfxFontEntryCallbacks {
605 public:
606 static tainted_gr<const void*> GrGetTable(
607 rlbox_sandbox_gr& sandbox, tainted_gr<const void*> /* aAppFaceHandle */,
608 tainted_gr<unsigned int> aName, tainted_gr<unsigned int*> aLen) {
609 gfxFontEntry* fontEntry = tl_grGetFontTableCallbackData;
610 *aLen = 0;
611 tainted_gr<const void*> ret = nullptr;
613 if (fontEntry) {
614 unsigned int fontTableKey = aName.unverified_safe_because(
615 "This is only being used to index into a hashmap, which is robust "
616 "for any value. No checks needed.");
617 gfxFontUtils::AutoHBBlob blob(fontEntry->GetFontTable(fontTableKey));
619 if (blob) {
620 unsigned int blobLength;
621 const void* tableData = hb_blob_get_data(blob, &blobLength);
622 // tableData is read-only data shared with the sandbox.
623 // Making a copy in sandbox memory
624 tainted_gr<void*> t_tableData = rlbox::sandbox_reinterpret_cast<void*>(
625 sandbox.malloc_in_sandbox<char>(blobLength));
626 if (t_tableData) {
627 rlbox::memcpy(sandbox, t_tableData, tableData, blobLength);
628 *aLen = blobLength;
629 ret = rlbox::sandbox_const_cast<const void*>(t_tableData);
634 return ret;
637 static void GrReleaseTable(rlbox_sandbox_gr& sandbox,
638 tainted_gr<const void*> /* aAppFaceHandle */,
639 tainted_gr<const void*> aTableBuffer) {
640 sandbox.free_in_sandbox(aTableBuffer);
643 static tainted_gr<float> GrGetAdvance(rlbox_sandbox_gr& sandbox,
644 tainted_gr<const void*> appFontHandle,
645 tainted_gr<uint16_t> glyphid) {
646 tainted_opaque_gr<float> ret = gfxGraphiteShaper::GrGetAdvance(
647 sandbox, appFontHandle.to_opaque(), glyphid.to_opaque());
648 return rlbox::from_opaque(ret);
652 struct gfxFontEntry::GrSandboxData {
653 rlbox_sandbox_gr sandbox;
654 sandbox_callback_gr<const void* (*)(const void*, unsigned int, unsigned int*)>
655 grGetTableCallback;
656 sandbox_callback_gr<void (*)(const void*, const void*)>
657 grReleaseTableCallback;
658 // Text Shapers register a callback to get glyph advances
659 sandbox_callback_gr<float (*)(const void*, uint16_t)>
660 grGetGlyphAdvanceCallback;
662 GrSandboxData() {
663 sandbox.create_sandbox();
664 grGetTableCallback =
665 sandbox.register_callback(gfxFontEntryCallbacks::GrGetTable);
666 grReleaseTableCallback =
667 sandbox.register_callback(gfxFontEntryCallbacks::GrReleaseTable);
668 grGetGlyphAdvanceCallback =
669 sandbox.register_callback(gfxFontEntryCallbacks::GrGetAdvance);
672 ~GrSandboxData() {
673 grGetTableCallback.unregister();
674 grReleaseTableCallback.unregister();
675 grGetGlyphAdvanceCallback.unregister();
676 sandbox.destroy_sandbox();
680 rlbox_sandbox_gr* gfxFontEntry::GetGrSandbox() {
681 AutoReadLock lock(mLock);
682 MOZ_ASSERT(mSandboxData != nullptr);
683 return &mSandboxData->sandbox;
686 sandbox_callback_gr<float (*)(const void*, uint16_t)>*
687 gfxFontEntry::GetGrSandboxAdvanceCallbackHandle() {
688 AutoReadLock lock(mLock);
689 MOZ_ASSERT(mSandboxData != nullptr);
690 return &mSandboxData->grGetGlyphAdvanceCallback;
693 tainted_opaque_gr<gr_face*> gfxFontEntry::GetGrFace() {
694 if (!mGrFaceInitialized) {
695 // When possible, the below code will use WASM as a sandboxing mechanism.
696 // At this time the wasm sandbox does not support threads.
697 // If Thebes is updated to make callst to the sandbox on multiple threaads,
698 // we need to make sure the underlying sandbox supports threading.
699 MOZ_ASSERT(NS_IsMainThread());
701 mSandboxData = new GrSandboxData();
703 auto p_faceOps = mSandboxData->sandbox.malloc_in_sandbox<gr_face_ops>();
704 if (!p_faceOps) {
705 MOZ_CRASH("Graphite sandbox memory allocation failed");
707 p_faceOps->size = sizeof(*p_faceOps);
708 p_faceOps->get_table = mSandboxData->grGetTableCallback;
709 p_faceOps->release_table = mSandboxData->grReleaseTableCallback;
711 tl_grGetFontTableCallbackData = this;
712 auto face = sandbox_invoke(
713 mSandboxData->sandbox, gr_make_face_with_ops,
714 // For security, we do not pass the callback data to this arg, and use
715 // a TLS var instead. However, gr_make_face_with_ops expects this to
716 // be a non null ptr. Therefore, we should pass some dummy non null
717 // pointer which will be passed to callbacks, but never used. Let's just
718 // pass p_faceOps again, as this is a non-null tainted pointer.
719 p_faceOps /* appFaceHandle */, p_faceOps, gr_face_default);
720 tl_grGetFontTableCallbackData = nullptr;
721 mGrFace = face.to_opaque();
722 mGrFaceInitialized = true;
723 mSandboxData->sandbox.free_in_sandbox(p_faceOps);
725 ++mGrFaceRefCnt;
726 return mGrFace;
729 void gfxFontEntry::ReleaseGrFace(tainted_opaque_gr<gr_face*> aFace) {
730 MOZ_ASSERT(
731 (rlbox::from_opaque(aFace) == rlbox::from_opaque(mGrFace))
732 .unverified_safe_because(
733 "This is safe as the only thing we are doing is comparing "
734 "addresses of two tainted pointers. Furthermore this is used "
735 "merely as a debugging aid in the debug builds. This function is "
736 "called only from the trusted Firefox code rather than the "
737 "untrusted libGraphite.")); // sanity-check
738 MOZ_ASSERT(mGrFaceRefCnt > 0);
739 if (--mGrFaceRefCnt == 0) {
740 auto t_mGrFace = rlbox::from_opaque(mGrFace);
742 tl_grGetFontTableCallbackData = this;
743 sandbox_invoke(mSandboxData->sandbox, gr_face_destroy, t_mGrFace);
744 tl_grGetFontTableCallbackData = nullptr;
746 t_mGrFace = nullptr;
747 mGrFace = t_mGrFace.to_opaque();
749 delete mSandboxData;
750 mSandboxData = nullptr;
752 mGrFaceInitialized = false;
756 void gfxFontEntry::DisconnectSVG() {
757 if (mSVGInitialized && mSVGGlyphs) {
758 mSVGGlyphs = nullptr;
759 mSVGInitialized = false;
763 bool gfxFontEntry::HasFontTable(uint32_t aTableTag) {
764 AutoTable table(this, aTableTag);
765 return table && hb_blob_get_length(table) > 0;
768 tainted_boolean_hint gfxFontEntry::HasGraphiteSpaceContextuals() {
769 LazyFlag flag = mHasGraphiteSpaceContextuals;
770 if (flag == LazyFlag::Uninitialized) {
771 auto face = GetGrFace();
772 auto t_face = rlbox::from_opaque(face);
773 if (t_face) {
774 tainted_gr<const gr_faceinfo*> faceInfo =
775 sandbox_invoke(mSandboxData->sandbox, gr_face_info, t_face, 0);
776 // Comparison with a value in sandboxed memory returns a
777 // tainted_boolean_hint, i.e. a "hint", since the value could be changed
778 // maliciously at any moment.
779 tainted_boolean_hint is_not_none =
780 faceInfo->space_contextuals != gr_faceinfo::gr_space_none;
781 flag = is_not_none.unverified_safe_because(
782 "Note ideally mHasGraphiteSpaceContextuals would be "
783 "tainted_boolean_hint, but RLBox does not yet support "
784 "bitfields, so it is not wrapped. However, its value is only "
785 "ever accessed through this function which returns a "
786 "tainted_boolean_hint, so unwrapping temporarily is safe. "
787 "We remove the wrapper now and re-add it below.")
788 ? LazyFlag::Yes
789 : LazyFlag::No;
791 ReleaseGrFace(face); // always balance GetGrFace, even if face is null
792 mHasGraphiteSpaceContextuals = flag;
795 return tainted_boolean_hint(flag == LazyFlag::Yes);
798 #define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag
800 static_assert(int(intl::Script::NUM_SCRIPT_CODES) <= FEATURE_SCRIPT_MASK,
801 "Too many script codes");
803 // high-order three bytes of tag with script in low-order byte
804 #define SCRIPT_FEATURE(s, tag) \
805 (((~FEATURE_SCRIPT_MASK) & (tag)) | \
806 ((FEATURE_SCRIPT_MASK) & static_cast<uint32_t>(s)))
808 bool gfxFontEntry::SupportsOpenTypeFeature(Script aScript,
809 uint32_t aFeatureTag) {
810 MutexAutoLock lock(mFeatureInfoLock);
811 if (!mSupportedFeatures) {
812 mSupportedFeatures = MakeUnique<nsTHashMap<nsUint32HashKey, bool>>();
815 // note: high-order three bytes *must* be unique for each feature
816 // listed below (see SCRIPT_FEATURE macro def'n)
817 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'm', 'c', 'p') ||
818 aFeatureTag == HB_TAG('c', '2', 's', 'c') ||
819 aFeatureTag == HB_TAG('p', 'c', 'a', 'p') ||
820 aFeatureTag == HB_TAG('c', '2', 'p', 'c') ||
821 aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
822 aFeatureTag == HB_TAG('s', 'u', 'b', 's') ||
823 aFeatureTag == HB_TAG('v', 'e', 'r', 't'),
824 "use of unknown feature tag");
826 // note: graphite feature support uses the last script index
827 NS_ASSERTION(int(aScript) < FEATURE_SCRIPT_MASK - 1,
828 "need to bump the size of the feature shift");
830 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
831 return mSupportedFeatures->LookupOrInsertWith(scriptFeature, [&] {
832 bool result = false;
833 auto face(GetHBFace());
835 if (hb_ot_layout_has_substitution(face)) {
836 hb_script_t hbScript =
837 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
839 // Get the OpenType tag(s) that match this script code
840 unsigned int scriptCount = 4;
841 hb_tag_t scriptTags[4];
842 hb_ot_tags_from_script_and_language(hbScript, HB_LANGUAGE_INVALID,
843 &scriptCount, scriptTags, nullptr,
844 nullptr);
846 // Append DEFAULT to the returned tags, if room
847 if (scriptCount < 4) {
848 scriptTags[scriptCount++] = HB_OT_TAG_DEFAULT_SCRIPT;
851 // Now check for 'smcp' under the first of those scripts that is present
852 const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
853 result = std::any_of(scriptTags, scriptTags + scriptCount,
854 [&](const hb_tag_t& scriptTag) {
855 unsigned int scriptIndex;
856 return hb_ot_layout_table_find_script(
857 face, kGSUB, scriptTag, &scriptIndex) &&
858 hb_ot_layout_language_find_feature(
859 face, kGSUB, scriptIndex,
860 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
861 aFeatureTag, nullptr);
865 return result;
869 const hb_set_t* gfxFontEntry::InputsForOpenTypeFeature(Script aScript,
870 uint32_t aFeatureTag) {
871 MutexAutoLock lock(mFeatureInfoLock);
872 if (!mFeatureInputs) {
873 mFeatureInputs = MakeUnique<nsTHashMap<nsUint32HashKey, hb_set_t*>>();
876 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
877 aFeatureTag == HB_TAG('s', 'u', 'b', 's') ||
878 aFeatureTag == HB_TAG('v', 'e', 'r', 't'),
879 "use of unknown feature tag");
881 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
882 hb_set_t* inputGlyphs;
883 if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
884 return inputGlyphs;
887 inputGlyphs = hb_set_create();
889 auto face(GetHBFace());
891 if (hb_ot_layout_has_substitution(face)) {
892 hb_script_t hbScript =
893 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
895 // Get the OpenType tag(s) that match this script code
896 unsigned int scriptCount = 4;
897 hb_tag_t scriptTags[5]; // space for null terminator
898 hb_ot_tags_from_script_and_language(hbScript, HB_LANGUAGE_INVALID,
899 &scriptCount, scriptTags, nullptr,
900 nullptr);
902 // Append DEFAULT to the returned tags, if room
903 if (scriptCount < 4) {
904 scriptTags[scriptCount++] = HB_OT_TAG_DEFAULT_SCRIPT;
906 scriptTags[scriptCount++] = 0;
908 const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
909 hb_tag_t features[2] = {aFeatureTag, HB_TAG_NONE};
910 hb_set_t* featurelookups = hb_set_create();
911 hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr, features,
912 featurelookups);
913 hb_codepoint_t index = -1;
914 while (hb_set_next(featurelookups, &index)) {
915 hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index, nullptr,
916 inputGlyphs, nullptr, nullptr);
918 hb_set_destroy(featurelookups);
921 mFeatureInputs->InsertOrUpdate(scriptFeature, inputGlyphs);
922 return inputGlyphs;
925 bool gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag) {
926 MutexAutoLock lock(mFeatureInfoLock);
928 if (!mSupportedFeatures) {
929 mSupportedFeatures = MakeUnique<nsTHashMap<nsUint32HashKey, bool>>();
932 // note: high-order three bytes *must* be unique for each feature
933 // listed below (see SCRIPT_FEATURE macro def'n)
934 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'm', 'c', 'p') ||
935 aFeatureTag == HB_TAG('c', '2', 's', 'c') ||
936 aFeatureTag == HB_TAG('p', 'c', 'a', 'p') ||
937 aFeatureTag == HB_TAG('c', '2', 'p', 'c') ||
938 aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
939 aFeatureTag == HB_TAG('s', 'u', 'b', 's'),
940 "use of unknown feature tag");
942 // graphite feature check uses the last script slot
943 uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag);
944 bool result;
945 if (mSupportedFeatures->Get(scriptFeature, &result)) {
946 return result;
949 auto face = GetGrFace();
950 auto t_face = rlbox::from_opaque(face);
951 result = t_face ? sandbox_invoke(mSandboxData->sandbox, gr_face_find_fref,
952 t_face, aFeatureTag) != nullptr
953 : false;
954 ReleaseGrFace(face);
956 mSupportedFeatures->InsertOrUpdate(scriptFeature, result);
958 return result;
961 void gfxFontEntry::GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo) {
962 // TODO: implement alternative code path for graphite fonts
964 auto autoFace(GetHBFace());
965 // Expose the raw hb_face_t to be captured by the lambdas (not the
966 // AutoHBFace wrapper).
967 hb_face_t* face = autoFace;
969 // Get the list of features for a specific <script,langSys> pair and
970 // append them to aFeatureInfo.
971 auto collectForLang = [=, &aFeatureInfo](
972 hb_tag_t aTableTag, unsigned int aScript,
973 hb_tag_t aScriptTag, unsigned int aLang,
974 hb_tag_t aLangTag) {
975 unsigned int featCount = hb_ot_layout_language_get_feature_tags(
976 face, aTableTag, aScript, aLang, 0, nullptr, nullptr);
977 AutoTArray<hb_tag_t, 32> featTags;
978 featTags.SetLength(featCount);
979 hb_ot_layout_language_get_feature_tags(face, aTableTag, aScript, aLang, 0,
980 &featCount, featTags.Elements());
981 MOZ_ASSERT(featCount <= featTags.Length());
982 // Just in case HB didn't fill featTags (i.e. in case it returned fewer
983 // tags than it promised), we truncate at the length it says it filled:
984 featTags.SetLength(featCount);
985 for (hb_tag_t t : featTags) {
986 aFeatureInfo.AppendElement(gfxFontFeatureInfo{t, aScriptTag, aLangTag});
990 // Iterate over the language systems supported by a given script,
991 // and call collectForLang for each of them.
992 auto collectForScript = [=](hb_tag_t aTableTag, unsigned int aScript,
993 hb_tag_t aScriptTag) {
994 collectForLang(aTableTag, aScript, aScriptTag,
995 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
996 HB_TAG('d', 'f', 'l', 't'));
997 unsigned int langCount = hb_ot_layout_script_get_language_tags(
998 face, aTableTag, aScript, 0, nullptr, nullptr);
999 AutoTArray<hb_tag_t, 32> langTags;
1000 langTags.SetLength(langCount);
1001 hb_ot_layout_script_get_language_tags(face, aTableTag, aScript, 0,
1002 &langCount, langTags.Elements());
1003 MOZ_ASSERT(langCount <= langTags.Length());
1004 langTags.SetLength(langCount);
1005 for (unsigned int lang = 0; lang < langCount; ++lang) {
1006 collectForLang(aTableTag, aScript, aScriptTag, lang, langTags[lang]);
1010 // Iterate over the scripts supported by a table (GSUB or GPOS), and call
1011 // collectForScript for each of them.
1012 auto collectForTable = [=](hb_tag_t aTableTag) {
1013 unsigned int scriptCount = hb_ot_layout_table_get_script_tags(
1014 face, aTableTag, 0, nullptr, nullptr);
1015 AutoTArray<hb_tag_t, 32> scriptTags;
1016 scriptTags.SetLength(scriptCount);
1017 hb_ot_layout_table_get_script_tags(face, aTableTag, 0, &scriptCount,
1018 scriptTags.Elements());
1019 MOZ_ASSERT(scriptCount <= scriptTags.Length());
1020 scriptTags.SetLength(scriptCount);
1021 for (unsigned int script = 0; script < scriptCount; ++script) {
1022 collectForScript(aTableTag, script, scriptTags[script]);
1026 // Collect all OpenType Layout features, both substitution and positioning,
1027 // supported by the font resource.
1028 collectForTable(HB_TAG('G', 'S', 'U', 'B'));
1029 collectForTable(HB_TAG('G', 'P', 'O', 'S'));
1032 typedef struct {
1033 AutoSwap_PRUint32 version;
1034 AutoSwap_PRUint16 format;
1035 AutoSwap_PRUint16 horizOffset;
1036 AutoSwap_PRUint16 vertOffset;
1037 AutoSwap_PRUint16 reserved;
1038 // TrackData horizData;
1039 // TrackData vertData;
1040 } TrakHeader;
1042 typedef struct {
1043 AutoSwap_PRUint16 nTracks;
1044 AutoSwap_PRUint16 nSizes;
1045 AutoSwap_PRUint32 sizeTableOffset;
1046 // trackTableEntry trackTable[];
1047 // fixed32 sizeTable[];
1048 } TrackData;
1050 typedef struct {
1051 AutoSwap_PRUint32 track;
1052 AutoSwap_PRUint16 nameIndex;
1053 AutoSwap_PRUint16 offset;
1054 } TrackTableEntry;
1056 bool gfxFontEntry::HasTrackingTable() {
1057 if (!TrakTableInitialized()) {
1058 hb_blob_t* trak = GetFontTable(TRUETYPE_TAG('t', 'r', 'a', 'k'));
1059 if (trak) {
1060 // mTrakTable itself is atomic, but we also want to set the auxiliary
1061 // pointers mTrakValues and mTrakSizeTable, so we take a lock here to
1062 // avoid racing with another thread also initializing the same values.
1063 AutoWriteLock lock(mLock);
1064 if (!mTrakTable.compareExchange(kTrakTableUninitialized, trak)) {
1065 hb_blob_destroy(trak);
1066 } else if (!ParseTrakTable()) {
1067 hb_blob_destroy(mTrakTable.exchange(nullptr));
1069 } else {
1070 mTrakTable.exchange(nullptr);
1073 return GetTrakTable() != nullptr;
1076 bool gfxFontEntry::ParseTrakTable() {
1077 // Check table validity and set up the subtable pointers we need;
1078 // if 'trak' table is invalid, or doesn't contain a 'normal' track,
1079 // return false to tell the caller not to try using it.
1080 unsigned int len;
1081 const char* data = hb_blob_get_data(GetTrakTable(), &len);
1082 if (len < sizeof(TrakHeader)) {
1083 return false;
1085 auto trak = reinterpret_cast<const TrakHeader*>(data);
1086 uint16_t horizOffset = trak->horizOffset;
1087 if (trak->version != 0x00010000 || uint16_t(trak->format) != 0 ||
1088 horizOffset == 0 || uint16_t(trak->reserved) != 0) {
1089 return false;
1091 // Find the horizontal trackData, and check it doesn't overrun the buffer.
1092 if (horizOffset > len - sizeof(TrackData)) {
1093 return false;
1095 auto trackData = reinterpret_cast<const TrackData*>(data + horizOffset);
1096 uint16_t nTracks = trackData->nTracks;
1097 mNumTrakSizes = trackData->nSizes;
1098 if (nTracks == 0 || mNumTrakSizes < 2) {
1099 return false;
1101 uint32_t sizeTableOffset = trackData->sizeTableOffset;
1102 // Find the trackTable, and check it doesn't overrun the buffer.
1103 if (horizOffset >
1104 len - (sizeof(TrackData) + nTracks * sizeof(TrackTableEntry))) {
1105 return false;
1107 auto trackTable = reinterpret_cast<const TrackTableEntry*>(
1108 data + horizOffset + sizeof(TrackData));
1109 // Look for 'normal' tracking, bail out if no such track is present.
1110 unsigned trackIndex;
1111 for (trackIndex = 0; trackIndex < nTracks; ++trackIndex) {
1112 if (trackTable[trackIndex].track == 0x00000000) {
1113 break;
1116 if (trackIndex == nTracks) {
1117 return false;
1119 // Find list of tracking values, and check they won't overrun.
1120 uint16_t offset = trackTable[trackIndex].offset;
1121 if (offset > len - mNumTrakSizes * sizeof(uint16_t)) {
1122 return false;
1124 mTrakValues = reinterpret_cast<const AutoSwap_PRInt16*>(data + offset);
1125 // Find the size subtable, and check it doesn't overrun the buffer.
1126 mTrakSizeTable =
1127 reinterpret_cast<const AutoSwap_PRInt32*>(data + sizeTableOffset);
1128 if (mTrakSizeTable + mNumTrakSizes >
1129 reinterpret_cast<const AutoSwap_PRInt32*>(data + len)) {
1130 return false;
1132 return true;
1135 gfxFloat gfxFontEntry::TrackingForCSSPx(gfxFloat aSize) const {
1136 // No locking because this does read-only access of fields that are inert
1137 // once initialized.
1138 MOZ_ASSERT(TrakTableInitialized() && mTrakTable && mTrakValues &&
1139 mTrakSizeTable);
1141 // Find index of first sizeTable entry that is >= the requested size.
1142 int32_t fixedSize = int32_t(aSize * 65536.0); // float -> 16.16 fixed-point
1143 unsigned sizeIndex;
1144 for (sizeIndex = 0; sizeIndex < mNumTrakSizes; ++sizeIndex) {
1145 if (mTrakSizeTable[sizeIndex] >= fixedSize) {
1146 break;
1149 // Return the tracking value for the requested size, or an interpolated
1150 // value if the exact size isn't found.
1151 if (sizeIndex == mNumTrakSizes) {
1152 // Request is larger than last entry in the table, so just use that.
1153 // (We don't attempt to extrapolate more extreme tracking values than
1154 // the largest or smallest present in the table.)
1155 return int16_t(mTrakValues[mNumTrakSizes - 1]);
1157 if (sizeIndex == 0 || mTrakSizeTable[sizeIndex] == fixedSize) {
1158 // Found an exact match, or size was smaller than the first entry.
1159 return int16_t(mTrakValues[sizeIndex]);
1161 // Requested size falls between two entries: interpolate value.
1162 double s0 = mTrakSizeTable[sizeIndex - 1] / 65536.0; // 16.16 -> float
1163 double s1 = mTrakSizeTable[sizeIndex] / 65536.0;
1164 double t = (aSize - s0) / (s1 - s0);
1165 return (1.0 - t) * int16_t(mTrakValues[sizeIndex - 1]) +
1166 t * int16_t(mTrakValues[sizeIndex]);
1169 void gfxFontEntry::SetupVariationRanges() {
1170 // No locking because this is done during initialization before any other
1171 // thread has access to the entry.
1172 if (!gfxPlatform::HasVariationFontSupport() ||
1173 !StaticPrefs::layout_css_font_variations_enabled() || !HasVariations() ||
1174 IsUserFont()) {
1175 return;
1177 AutoTArray<gfxFontVariationAxis, 4> axes;
1178 GetVariationAxes(axes);
1179 for (const auto& axis : axes) {
1180 switch (axis.mTag) {
1181 case HB_TAG('w', 'g', 'h', 't'):
1182 // If the axis range looks like it doesn't fit the CSS font-weight
1183 // scale, we don't hook up the high-level property, and we mark
1184 // the face (in mRangeFlags) as having non-standard weight. This
1185 // means we won't map CSS font-weight to the axis. Setting 'wght'
1186 // with font-variation-settings will still work.
1187 // Strictly speaking, the min value should be checked against 1.0,
1188 // not 0.0, but we'll allow font makers that amount of leeway, as
1189 // in practice a number of fonts seem to use 0..1000.
1190 if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
1191 // If axis.mMaxValue is less than the default weight we already
1192 // set up, assume the axis has a non-standard range (like Skia)
1193 // and don't try to map it.
1194 Weight().Min() <= FontWeight::FromFloat(axis.mMaxValue)) {
1195 if (FontWeight::FromFloat(axis.mDefaultValue) != Weight().Min()) {
1196 mStandardFace = false;
1198 mWeightRange =
1199 WeightRange(FontWeight::FromFloat(std::max(1.0f, axis.mMinValue)),
1200 FontWeight::FromFloat(axis.mMaxValue));
1201 } else {
1202 mRangeFlags |= RangeFlags::eNonCSSWeight;
1204 break;
1206 case HB_TAG('w', 'd', 't', 'h'):
1207 if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
1208 Stretch().Min() <= FontStretch::FromFloat(axis.mMaxValue)) {
1209 if (FontStretch::FromFloat(axis.mDefaultValue) != Stretch().Min()) {
1210 mStandardFace = false;
1212 mStretchRange = StretchRange(FontStretch::FromFloat(axis.mMinValue),
1213 FontStretch::FromFloat(axis.mMaxValue));
1214 } else {
1215 mRangeFlags |= RangeFlags::eNonCSSStretch;
1217 break;
1219 case HB_TAG('s', 'l', 'n', 't'):
1220 if (axis.mMinValue >= -90.0f && axis.mMaxValue <= 90.0f) {
1221 if (FontSlantStyle::FromFloat(axis.mDefaultValue) !=
1222 SlantStyle().Min()) {
1223 mStandardFace = false;
1225 // OpenType and CSS measure angles in opposite directions, so we
1226 // have to flip signs and swap min/max when setting up the CSS
1227 // font-style range here.
1228 mStyleRange =
1229 SlantStyleRange(FontSlantStyle::FromFloat(-axis.mMaxValue),
1230 FontSlantStyle::FromFloat(-axis.mMinValue));
1232 break;
1234 case HB_TAG('i', 't', 'a', 'l'):
1235 if (axis.mMinValue <= 0.0f && axis.mMaxValue >= 1.0f) {
1236 if (axis.mDefaultValue != 0.0f) {
1237 mStandardFace = false;
1239 mStyleRange =
1240 SlantStyleRange(FontSlantStyle::NORMAL, FontSlantStyle::ITALIC);
1242 break;
1244 default:
1245 continue;
1250 void gfxFontEntry::CheckForVariationAxes() {
1251 if (mCheckedForVariationAxes) {
1252 return;
1254 mCheckedForVariationAxes = true;
1255 if (HasVariations()) {
1256 AutoTArray<gfxFontVariationAxis, 4> axes;
1257 GetVariationAxes(axes);
1258 for (const auto& axis : axes) {
1259 if (axis.mTag == HB_TAG('w', 'g', 'h', 't') && axis.mMaxValue >= 600.0f) {
1260 mRangeFlags |= RangeFlags::eBoldVariableWeight;
1261 } else if (axis.mTag == HB_TAG('i', 't', 'a', 'l') &&
1262 axis.mMaxValue >= 1.0f) {
1263 mRangeFlags |= RangeFlags::eItalicVariation;
1264 } else if (axis.mTag == HB_TAG('s', 'l', 'n', 't')) {
1265 mRangeFlags |= RangeFlags::eSlantVariation;
1266 } else if (axis.mTag == HB_TAG('o', 'p', 's', 'z')) {
1267 mRangeFlags |= RangeFlags::eOpticalSize;
1273 bool gfxFontEntry::HasBoldVariableWeight() {
1274 MOZ_ASSERT(!mIsUserFontContainer,
1275 "should not be called for user-font containers!");
1276 CheckForVariationAxes();
1277 return bool(mRangeFlags & RangeFlags::eBoldVariableWeight);
1280 bool gfxFontEntry::HasItalicVariation() {
1281 MOZ_ASSERT(!mIsUserFontContainer,
1282 "should not be called for user-font containers!");
1283 CheckForVariationAxes();
1284 return bool(mRangeFlags & RangeFlags::eItalicVariation);
1287 bool gfxFontEntry::HasSlantVariation() {
1288 MOZ_ASSERT(!mIsUserFontContainer,
1289 "should not be called for user-font containers!");
1290 CheckForVariationAxes();
1291 return bool(mRangeFlags & RangeFlags::eSlantVariation);
1294 bool gfxFontEntry::HasOpticalSize() {
1295 MOZ_ASSERT(!mIsUserFontContainer,
1296 "should not be called for user-font containers!");
1297 CheckForVariationAxes();
1298 return bool(mRangeFlags & RangeFlags::eOpticalSize);
1301 void gfxFontEntry::GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
1302 const gfxFontStyle& aStyle) {
1303 if (!gfxPlatform::HasVariationFontSupport() ||
1304 !StaticPrefs::layout_css_font_variations_enabled()) {
1305 return;
1308 if (!HasVariations()) {
1309 return;
1312 // Resolve high-level CSS properties from the requested style
1313 // (font-{style,weight,stretch}) to the appropriate variations.
1314 // The value used is clamped to the range available in the font face,
1315 // unless the face is a user font where no explicit descriptor was
1316 // given, indicated by the corresponding 'auto' range-flag.
1318 // We don't do these mappings if the font entry has weight and/or stretch
1319 // ranges that do not appear to use the CSS property scale. Some older
1320 // fonts created for QuickDrawGX/AAT may use "normalized" values where the
1321 // standard variation is 1.0 rather than 400.0 (weight) or 100.0 (stretch).
1323 if (!(mRangeFlags & RangeFlags::eNonCSSWeight)) {
1324 float weight = (IsUserFont() && (mRangeFlags & RangeFlags::eAutoWeight))
1325 ? aStyle.weight.ToFloat()
1326 : Weight().Clamp(aStyle.weight).ToFloat();
1327 aResult.AppendElement(gfxFontVariation{HB_TAG('w', 'g', 'h', 't'), weight});
1330 if (!(mRangeFlags & RangeFlags::eNonCSSStretch)) {
1331 float stretch = (IsUserFont() && (mRangeFlags & RangeFlags::eAutoStretch))
1332 ? aStyle.stretch.ToFloat()
1333 : Stretch().Clamp(aStyle.stretch).ToFloat();
1334 aResult.AppendElement(
1335 gfxFontVariation{HB_TAG('w', 'd', 't', 'h'), stretch});
1338 if (aStyle.style.IsItalic() && SupportsItalic()) {
1339 // The 'ital' axis is normally a binary toggle; intermediate values
1340 // can only be set using font-variation-settings.
1341 aResult.AppendElement(gfxFontVariation{HB_TAG('i', 't', 'a', 'l'), 1.0f});
1342 } else if (aStyle.style != StyleFontStyle::NORMAL && HasSlantVariation()) {
1343 // Figure out what slant angle we should try to match from the
1344 // requested style.
1345 float angle = aStyle.style.SlantAngle();
1346 // Clamp to the available range, unless the face is a user font
1347 // with no explicit descriptor.
1348 if (!(IsUserFont() && (mRangeFlags & RangeFlags::eAutoSlantStyle))) {
1349 angle = SlantStyle().Clamp(FontSlantStyle::FromFloat(angle)).SlantAngle();
1351 // OpenType and CSS measure angles in opposite directions, so we have to
1352 // invert the sign of the CSS oblique value when setting OpenType 'slnt'.
1353 aResult.AppendElement(gfxFontVariation{HB_TAG('s', 'l', 'n', 't'), -angle});
1356 struct TagEquals {
1357 bool Equals(const gfxFontVariation& aIter, uint32_t aTag) const {
1358 return aIter.mTag == aTag;
1362 auto replaceOrAppend = [&aResult](const gfxFontVariation& aSetting) {
1363 auto index = aResult.IndexOf(aSetting.mTag, 0, TagEquals());
1364 if (index == aResult.NoIndex) {
1365 aResult.AppendElement(aSetting);
1366 } else {
1367 aResult[index].mValue = aSetting.mValue;
1371 // The low-level font-variation-settings descriptor from @font-face,
1372 // if present, takes precedence over automatic variation settings
1373 // from high-level properties.
1374 for (const auto& v : mVariationSettings) {
1375 replaceOrAppend(v);
1378 // And the low-level font-variation-settings property takes precedence
1379 // over the descriptor.
1380 for (const auto& v : aStyle.variationSettings) {
1381 replaceOrAppend(v);
1384 // If there's no explicit opsz in the settings, apply 'auto' value.
1385 if (HasOpticalSize() && aStyle.autoOpticalSize >= 0.0f) {
1386 const uint32_t kOpszTag = HB_TAG('o', 'p', 's', 'z');
1387 auto index = aResult.IndexOf(kOpszTag, 0, TagEquals());
1388 if (index == aResult.NoIndex) {
1389 float value = aStyle.autoOpticalSize * mSizeAdjust;
1390 aResult.AppendElement(gfxFontVariation{kOpszTag, value});
1395 size_t gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(
1396 mozilla::MallocSizeOf aMallocSizeOf) const {
1397 size_t n = 0;
1398 if (mBlob) {
1399 n += aMallocSizeOf(mBlob);
1401 if (mSharedBlobData) {
1402 n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
1404 return n;
1407 void gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1408 FontListSizes* aSizes) const {
1409 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1411 // cmaps are shared so only non-shared cmaps are included here
1412 if (mCharacterMap && GetCharacterMap()->mBuildOnTheFly) {
1413 aSizes->mCharMapsSize +=
1414 GetCharacterMap()->SizeOfIncludingThis(aMallocSizeOf);
1417 AutoReadLock lock(mLock);
1418 if (mFontTableCache) {
1419 aSizes->mFontTableCacheSize +=
1420 GetFontTableCache()->SizeOfIncludingThis(aMallocSizeOf);
1424 // If the font has UVS data, we count that as part of the character map.
1425 if (mUVSData) {
1426 aSizes->mCharMapsSize += aMallocSizeOf(GetUVSData());
1429 // The following, if present, are essentially cached forms of font table
1430 // data, so we'll accumulate them together with the basic table cache.
1431 if (mUserFontData) {
1432 aSizes->mFontTableCacheSize +=
1433 mUserFontData->SizeOfIncludingThis(aMallocSizeOf);
1435 if (mSVGGlyphs) {
1436 aSizes->mFontTableCacheSize +=
1437 GetSVGGlyphs()->SizeOfIncludingThis(aMallocSizeOf);
1441 MutexAutoLock lock(mFeatureInfoLock);
1442 if (mSupportedFeatures) {
1443 aSizes->mFontTableCacheSize +=
1444 mSupportedFeatures->ShallowSizeOfIncludingThis(aMallocSizeOf);
1446 if (mFeatureInputs) {
1447 aSizes->mFontTableCacheSize +=
1448 mFeatureInputs->ShallowSizeOfIncludingThis(aMallocSizeOf);
1449 // XXX Can't this simply be
1450 // aSizes->mFontTableCacheSize += 8192 * mFeatureInputs->Count();
1451 for (auto iter = mFeatureInputs->ConstIter(); !iter.Done(); iter.Next()) {
1452 // There's no API to get the real size of an hb_set, so we'll use
1453 // an approximation based on knowledge of the implementation.
1454 aSizes->mFontTableCacheSize += 8192; // vector of 64K bits
1458 // We don't include the size of mCOLR/mCPAL here, because (depending on the
1459 // font backend implementation) they will either wrap blocks of data owned
1460 // by the system (and potentially shared), or tables that are in our font
1461 // table cache and therefore already counted.
1464 void gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1465 FontListSizes* aSizes) const {
1466 aSizes->mFontListSize += aMallocSizeOf(this);
1467 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1470 // This is used to report the size of an individual downloaded font in the
1471 // user font cache. (Fonts that are part of the platform font list accumulate
1472 // their sizes to the font list's reporter using the AddSizeOf... methods
1473 // above.)
1474 size_t gfxFontEntry::ComputedSizeOfExcludingThis(
1475 MallocSizeOf aMallocSizeOf) const {
1476 FontListSizes s = {0};
1477 AddSizeOfExcludingThis(aMallocSizeOf, &s);
1479 // When reporting memory used for the main platform font list,
1480 // where we're typically summing the totals for a few hundred font faces,
1481 // we report the fields of FontListSizes separately.
1482 // But for downloaded user fonts, the actual resource data (added below)
1483 // will dominate, and the minor overhead of these pieces isn't worth
1484 // splitting out for an individual font.
1485 size_t result = s.mFontListSize + s.mFontTableCacheSize + s.mCharMapsSize;
1487 if (mIsDataUserFont) {
1488 MOZ_ASSERT(mComputedSizeOfUserFont > 0, "user font with no data?");
1489 result += mComputedSizeOfUserFont;
1492 return result;
1495 //////////////////////////////////////////////////////////////////////////////
1497 // class gfxFontFamily
1499 //////////////////////////////////////////////////////////////////////////////
1501 // we consider faces with mStandardFace == true to be "less than" those with
1502 // false, because during style matching, earlier entries are tried first
1503 class FontEntryStandardFaceComparator {
1504 public:
1505 bool Equals(const RefPtr<gfxFontEntry>& a,
1506 const RefPtr<gfxFontEntry>& b) const {
1507 return a->mStandardFace == b->mStandardFace;
1509 bool LessThan(const RefPtr<gfxFontEntry>& a,
1510 const RefPtr<gfxFontEntry>& b) const {
1511 return (a->mStandardFace == true && b->mStandardFace == false);
1515 void gfxFontFamily::SortAvailableFonts() {
1516 MOZ_ASSERT(mLock.LockedForWritingByCurrentThread());
1517 mAvailableFonts.Sort(FontEntryStandardFaceComparator());
1520 bool gfxFontFamily::HasOtherFamilyNames() {
1521 // need to read in other family names to determine this
1522 if (!mOtherFamilyNamesInitialized) {
1523 ReadOtherFamilyNames(
1524 gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
1526 return mHasOtherFamilyNames;
1529 gfxFontEntry* gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
1530 bool aIgnoreSizeTolerance) {
1531 AutoTArray<gfxFontEntry*, 4> matched;
1532 FindAllFontsForStyle(aFontStyle, matched, aIgnoreSizeTolerance);
1533 if (!matched.IsEmpty()) {
1534 return matched[0];
1536 return nullptr;
1539 static inline double WeightStyleStretchDistance(
1540 gfxFontEntry* aFontEntry, const gfxFontStyle& aTargetStyle) {
1541 double stretchDist =
1542 StretchDistance(aFontEntry->Stretch(), aTargetStyle.stretch);
1543 double styleDist =
1544 StyleDistance(aFontEntry->SlantStyle(), aTargetStyle.style);
1545 double weightDist = WeightDistance(aFontEntry->Weight(), aTargetStyle.weight);
1547 // Sanity-check that the distances are within the expected range
1548 // (update if implementation of the distance functions is changed).
1549 MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
1550 MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
1551 MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
1553 // weight/style/stretch priority: stretch >> style >> weight
1554 // so we multiply the stretch and style values to make them dominate
1555 // the result
1556 return stretchDist * kStretchFactor + styleDist * kStyleFactor +
1557 weightDist * kWeightFactor;
1560 void gfxFontFamily::FindAllFontsForStyle(
1561 const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
1562 bool aIgnoreSizeTolerance) {
1563 if (!mHasStyles) {
1564 FindStyleVariations(); // collect faces for the family, if not already
1565 // done
1568 AutoReadLock lock(mLock);
1570 NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
1571 NS_ASSERTION(aFontEntryList.IsEmpty(), "non-empty fontlist passed in");
1573 gfxFontEntry* fe = nullptr;
1575 // If the family has only one face, we simply return it; no further
1576 // checking needed
1577 uint32_t count = mAvailableFonts.Length();
1578 if (count == 1) {
1579 fe = mAvailableFonts[0];
1580 aFontEntryList.AppendElement(fe);
1581 return;
1584 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
1585 // or some subset of these. In this case, we have exactly 4 entries in
1586 // mAvailableFonts, stored in the above order; note that some of the entries
1587 // may be nullptr. We can then pick the required entry based on whether the
1588 // request is for bold or non-bold, italic or non-italic, without running the
1589 // more complex matching algorithm used for larger families with many weights
1590 // and/or widths.
1592 if (mIsSimpleFamily) {
1593 // Family has no more than the "standard" 4 faces, at fixed indexes;
1594 // calculate which one we want.
1595 // Note that we cannot simply return it as not all 4 faces are necessarily
1596 // present.
1597 bool wantBold = aFontStyle.weight >= FontWeight::FromInt(600);
1598 bool wantItalic = !aFontStyle.style.IsNormal();
1599 uint8_t faceIndex =
1600 (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
1602 // if the desired style is available, return it directly
1603 fe = mAvailableFonts[faceIndex];
1604 if (fe) {
1605 aFontEntryList.AppendElement(fe);
1606 return;
1609 // order to check fallback faces in a simple family, depending on requested
1610 // style
1611 static const uint8_t simpleFallbacks[4][3] = {
1612 {kBoldFaceIndex, kItalicFaceIndex,
1613 kBoldItalicFaceIndex}, // fallbacks for Regular
1614 {kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex}, // Bold
1615 {kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex}, // Italic
1616 {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex} // BoldItalic
1618 const uint8_t* order = simpleFallbacks[faceIndex];
1620 for (uint8_t trial = 0; trial < 3; ++trial) {
1621 // check remaining faces in order of preference to find the first that
1622 // actually exists
1623 fe = mAvailableFonts[order[trial]];
1624 if (fe) {
1625 aFontEntryList.AppendElement(fe);
1626 return;
1630 // this can't happen unless we have totally broken the font-list manager!
1631 MOZ_ASSERT_UNREACHABLE("no face found in simple font family!");
1634 // Pick the font(s) that are closest to the desired weight, style, and
1635 // stretch. Iterate over all fonts, measuring the weight/style distance.
1636 // Because of unicode-range values, there may be more than one font for a
1637 // given but the 99% use case is only a single font entry per
1638 // weight/style/stretch distance value. To optimize this, only add entries
1639 // to the matched font array when another entry already has the same
1640 // weight/style/stretch distance and add the last matched font entry. For
1641 // normal platform fonts with a single font entry for each
1642 // weight/style/stretch combination, only the last matched font entry will
1643 // be added.
1645 double minDistance = INFINITY;
1646 gfxFontEntry* matched = nullptr;
1647 // iterate in forward order so that faces like 'Bold' are matched before
1648 // matching style distance faces such as 'Bold Outline' (see bug 1185812)
1649 for (uint32_t i = 0; i < count; i++) {
1650 fe = mAvailableFonts[i];
1651 // weight/style/stretch priority: stretch >> style >> weight
1652 double distance = WeightStyleStretchDistance(fe, aFontStyle);
1653 if (distance < minDistance) {
1654 matched = fe;
1655 if (!aFontEntryList.IsEmpty()) {
1656 aFontEntryList.Clear();
1658 minDistance = distance;
1659 } else if (distance == minDistance) {
1660 if (matched) {
1661 aFontEntryList.AppendElement(matched);
1663 matched = fe;
1667 NS_ASSERTION(matched, "didn't match a font within a family");
1669 if (matched) {
1670 aFontEntryList.AppendElement(matched);
1674 void gfxFontFamily::CheckForSimpleFamily() {
1675 MOZ_ASSERT(mLock.LockedForWritingByCurrentThread());
1676 // already checked this family
1677 if (mIsSimpleFamily) {
1678 return;
1681 uint32_t count = mAvailableFonts.Length();
1682 if (count > 4 || count == 0) {
1683 return; // can't be "simple" if there are >4 faces;
1684 // if none then the family is unusable anyway
1687 if (count == 1) {
1688 mIsSimpleFamily = true;
1689 return;
1692 StretchRange firstStretch = mAvailableFonts[0]->Stretch();
1693 if (!firstStretch.IsSingle()) {
1694 return; // family with variation fonts is not considered "simple"
1697 gfxFontEntry* faces[4] = {0};
1698 for (uint8_t i = 0; i < count; ++i) {
1699 gfxFontEntry* fe = mAvailableFonts[i];
1700 if (fe->Stretch() != firstStretch || fe->IsOblique()) {
1701 // simple families don't have varying font-stretch or oblique
1702 return;
1704 if (!fe->Weight().IsSingle() || !fe->SlantStyle().IsSingle()) {
1705 return; // family with variation fonts is not considered "simple"
1707 uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
1708 (fe->SupportsBold() ? kBoldMask : 0);
1709 if (faces[faceIndex]) {
1710 return; // two faces resolve to the same slot; family isn't "simple"
1712 faces[faceIndex] = fe;
1715 // we have successfully slotted the available faces into the standard
1716 // 4-face framework
1717 mAvailableFonts.SetLength(4);
1718 for (uint8_t i = 0; i < 4; ++i) {
1719 if (mAvailableFonts[i].get() != faces[i]) {
1720 mAvailableFonts[i].swap(faces[i]);
1724 mIsSimpleFamily = true;
1727 #ifdef DEBUG
1728 bool gfxFontFamily::ContainsFace(gfxFontEntry* aFontEntry) {
1729 AutoReadLock lock(mLock);
1731 uint32_t i, numFonts = mAvailableFonts.Length();
1732 for (i = 0; i < numFonts; i++) {
1733 if (mAvailableFonts[i] == aFontEntry) {
1734 return true;
1736 // userfonts contain the actual real font entry
1737 if (mAvailableFonts[i] && mAvailableFonts[i]->mIsUserFontContainer) {
1738 gfxUserFontEntry* ufe =
1739 static_cast<gfxUserFontEntry*>(mAvailableFonts[i].get());
1740 if (ufe->GetPlatformFontEntry() == aFontEntry) {
1741 return true;
1745 return false;
1747 #endif
1749 void gfxFontFamily::LocalizedName(nsACString& aLocalizedName) {
1750 // just return the primary name; subclasses should override
1751 aLocalizedName = mName;
1754 void gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) {
1755 gfxPlatformFontList::PlatformFontList()->mLock.AssertCurrentThreadIn();
1758 AutoReadLock lock(mLock);
1759 if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
1760 // none of the faces in the family support the required char,
1761 // so bail out immediately
1762 return;
1766 nsCString charAndName;
1767 if (profiler_thread_is_being_profiled(
1768 Combine(ThreadProfilingFeatures::Sampling,
1769 ThreadProfilingFeatures::Markers))) {
1770 charAndName = nsPrintfCString("\\u%x %s", aMatchData->mCh, mName.get());
1772 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxFontFamily::FindFontForChar",
1773 LAYOUT, charAndName);
1775 AutoTArray<gfxFontEntry*, 4> entries;
1776 FindAllFontsForStyle(aMatchData->mStyle, entries,
1777 /*aIgnoreSizeTolerance*/ true);
1778 if (entries.IsEmpty()) {
1779 return;
1782 gfxFontEntry* fe = nullptr;
1783 float distance = INFINITY;
1785 for (auto e : entries) {
1786 if (e->SkipDuringSystemFallback()) {
1787 continue;
1790 aMatchData->mCmapsTested++;
1791 if (e->HasCharacter(aMatchData->mCh)) {
1792 aMatchData->mCount++;
1794 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
1796 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
1797 intl::Script script =
1798 intl::UnicodeProperties::GetScriptCode(aMatchData->mCh);
1799 MOZ_LOG(log, LogLevel::Debug,
1800 ("(textrun-systemfallback-fonts) char: u+%6.6x "
1801 "script: %d match: [%s]\n",
1802 aMatchData->mCh, int(script), e->Name().get()));
1805 fe = e;
1806 distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
1807 if (aMatchData->mPresentation != eFontPresentation::Any) {
1808 RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
1809 if (!font) {
1810 continue;
1812 bool hasColorGlyph =
1813 font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
1814 if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
1815 distance += kPresentationMismatch;
1818 break;
1822 if (!fe && !aMatchData->mStyle.IsNormalStyle()) {
1823 // If style/weight/stretch was not Normal, see if we can
1824 // fall back to a next-best face (e.g. Arial Black -> Bold,
1825 // or Arial Narrow -> Regular).
1826 GlobalFontMatch data(aMatchData->mCh, aMatchData->mNextCh,
1827 aMatchData->mStyle, aMatchData->mPresentation);
1828 SearchAllFontsForChar(&data);
1829 if (!data.mBestMatch) {
1830 return;
1832 fe = data.mBestMatch;
1833 distance = data.mMatchDistance;
1836 if (!fe) {
1837 return;
1840 if (distance < aMatchData->mMatchDistance ||
1841 (distance == aMatchData->mMatchDistance &&
1842 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
1843 aMatchData->mBestMatch = fe;
1844 aMatchData->mMatchedFamily = this;
1845 aMatchData->mMatchDistance = distance;
1849 void gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) {
1850 if (!mFamilyCharacterMapInitialized) {
1851 ReadAllCMAPs();
1853 AutoReadLock lock(mLock);
1854 if (!mFamilyCharacterMap.test(aMatchData->mCh)) {
1855 return;
1857 uint32_t i, numFonts = mAvailableFonts.Length();
1858 for (i = 0; i < numFonts; i++) {
1859 gfxFontEntry* fe = mAvailableFonts[i];
1860 if (fe && fe->HasCharacter(aMatchData->mCh)) {
1861 float distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
1862 if (aMatchData->mPresentation != eFontPresentation::Any) {
1863 RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
1864 if (!font) {
1865 continue;
1867 bool hasColorGlyph =
1868 font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
1869 if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
1870 distance += kPresentationMismatch;
1873 if (distance < aMatchData->mMatchDistance ||
1874 (distance == aMatchData->mMatchDistance &&
1875 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
1876 aMatchData->mBestMatch = fe;
1877 aMatchData->mMatchedFamily = this;
1878 aMatchData->mMatchDistance = distance;
1884 /*virtual*/
1885 gfxFontFamily::~gfxFontFamily() {
1886 // Should not be dropped by stylo, but the InitFontList thread might use
1887 // a transient gfxFontFamily and that's OK.
1888 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
1891 // returns true if other names were found, false otherwise
1892 bool gfxFontFamily::ReadOtherFamilyNamesForFace(
1893 gfxPlatformFontList* aPlatformFontList, hb_blob_t* aNameTable,
1894 bool useFullName) {
1895 uint32_t dataLength;
1896 const char* nameData = hb_blob_get_data(aNameTable, &dataLength);
1897 AutoTArray<nsCString, 4> otherFamilyNames;
1899 gfxFontUtils::ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
1900 otherFamilyNames, useFullName);
1902 if (!otherFamilyNames.IsEmpty()) {
1903 aPlatformFontList->AddOtherFamilyNames(this, otherFamilyNames);
1906 return !otherFamilyNames.IsEmpty();
1909 void gfxFontFamily::ReadOtherFamilyNames(
1910 gfxPlatformFontList* aPlatformFontList) {
1911 AutoWriteLock lock(mLock);
1912 if (mOtherFamilyNamesInitialized) {
1913 return;
1916 mOtherFamilyNamesInitialized = true;
1918 FindStyleVariationsLocked();
1920 // read in other family names for the first face in the list
1921 uint32_t i, numFonts = mAvailableFonts.Length();
1922 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
1924 for (i = 0; i < numFonts; ++i) {
1925 gfxFontEntry* fe = mAvailableFonts[i];
1926 if (!fe) {
1927 continue;
1929 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1930 if (!nameTable) {
1931 continue;
1933 mHasOtherFamilyNames =
1934 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1935 break;
1938 // read in other names for the first face in the list with the assumption
1939 // that if extra names don't exist in that face then they don't exist in
1940 // other faces for the same font
1941 if (!mHasOtherFamilyNames) {
1942 return;
1945 // read in names for all faces, needed to catch cases where fonts have
1946 // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
1947 for (; i < numFonts; i++) {
1948 gfxFontEntry* fe = mAvailableFonts[i];
1949 if (!fe) {
1950 continue;
1952 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1953 if (!nameTable) {
1954 continue;
1956 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1960 static bool LookForLegacyFamilyName(const nsACString& aCanonicalName,
1961 const char* aNameData, uint32_t aDataLength,
1962 nsACString& aLegacyName /* outparam */) {
1963 const gfxFontUtils::NameHeader* nameHeader =
1964 reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
1966 uint32_t nameCount = nameHeader->count;
1967 if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
1968 NS_WARNING("invalid font (name records)");
1969 return false;
1972 const gfxFontUtils::NameRecord* nameRecord =
1973 reinterpret_cast<const gfxFontUtils::NameRecord*>(
1974 aNameData + sizeof(gfxFontUtils::NameHeader));
1975 uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
1977 for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
1978 uint32_t nameLen = nameRecord->length;
1979 uint32_t nameOff = nameRecord->offset;
1981 if (stringsBase + nameOff + nameLen > aDataLength) {
1982 NS_WARNING("invalid font (name table strings)");
1983 return false;
1986 if (uint16_t(nameRecord->nameID) == gfxFontUtils::NAME_ID_FAMILY) {
1987 bool ok = gfxFontUtils::DecodeFontName(
1988 aNameData + stringsBase + nameOff, nameLen,
1989 uint32_t(nameRecord->platformID), uint32_t(nameRecord->encodingID),
1990 uint32_t(nameRecord->languageID), aLegacyName);
1991 // It's only a legacy name if it case-insensitively differs from the
1992 // canonical name (otherwise it would map to the same key).
1993 if (ok && !aLegacyName.Equals(aCanonicalName,
1994 nsCaseInsensitiveCStringComparator)) {
1995 return true;
1999 return false;
2002 bool gfxFontFamily::CheckForLegacyFamilyNames(gfxPlatformFontList* aFontList) {
2003 aFontList->mLock.AssertCurrentThreadIn();
2004 if (mCheckedForLegacyFamilyNames) {
2005 // we already did this, so there's nothing more to add
2006 return false;
2008 mCheckedForLegacyFamilyNames = true;
2009 bool added = false;
2010 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
2011 AutoTArray<RefPtr<gfxFontEntry>, 16> faces;
2013 // Take a local copy of the array of font entries, because it's possible
2014 // AddWithLegacyFamilyName will mutate it (and it needs to be able to take
2015 // an exclusive lock on the family to do so, so we release the read lock
2016 // here).
2017 AutoReadLock lock(mLock);
2018 faces.AppendElements(mAvailableFonts);
2020 for (const auto& fe : faces) {
2021 if (!fe) {
2022 continue;
2024 gfxFontEntry::AutoTable nameTable(fe, kNAME);
2025 if (!nameTable) {
2026 continue;
2028 nsAutoCString legacyName;
2029 uint32_t dataLength;
2030 const char* nameData = hb_blob_get_data(nameTable, &dataLength);
2031 if (LookForLegacyFamilyName(Name(), nameData, dataLength, legacyName)) {
2032 if (aFontList->AddWithLegacyFamilyName(legacyName, fe, mVisibility)) {
2033 added = true;
2037 return added;
2040 void gfxFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
2041 bool aNeedFullnamePostscriptNames,
2042 FontInfoData* aFontInfoData) {
2043 aPlatformFontList->mLock.AssertCurrentThreadIn();
2045 // if all needed names have already been read, skip
2046 if (mOtherFamilyNamesInitialized &&
2047 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2048 return;
2051 AutoWriteLock lock(mLock);
2053 bool asyncFontLoaderDisabled = false;
2055 if (!mOtherFamilyNamesInitialized && aFontInfoData &&
2056 aFontInfoData->mLoadOtherNames && !asyncFontLoaderDisabled) {
2057 const auto* otherFamilyNames = aFontInfoData->GetOtherFamilyNames(mName);
2058 if (otherFamilyNames && otherFamilyNames->Length()) {
2059 aPlatformFontList->AddOtherFamilyNames(this, *otherFamilyNames);
2061 mOtherFamilyNamesInitialized = true;
2064 // if all needed data has been initialized, return
2065 if (mOtherFamilyNamesInitialized &&
2066 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2067 return;
2070 FindStyleVariationsLocked(aFontInfoData);
2072 // check again, as style enumeration code may have loaded names
2073 if (mOtherFamilyNamesInitialized &&
2074 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2075 return;
2078 uint32_t i, numFonts = mAvailableFonts.Length();
2079 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
2081 bool firstTime = true, readAllFaces = false;
2082 for (i = 0; i < numFonts; ++i) {
2083 gfxFontEntry* fe = mAvailableFonts[i];
2084 if (!fe) {
2085 continue;
2088 nsAutoCString fullname, psname;
2089 bool foundFaceNames = false;
2090 if (!mFaceNamesInitialized && aNeedFullnamePostscriptNames &&
2091 aFontInfoData && aFontInfoData->mLoadFaceNames) {
2092 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
2093 if (!fullname.IsEmpty()) {
2094 aPlatformFontList->AddFullnameLocked(fe, fullname);
2096 if (!psname.IsEmpty()) {
2097 aPlatformFontList->AddPostscriptNameLocked(fe, psname);
2099 foundFaceNames = true;
2101 // found everything needed? skip to next font
2102 if (mOtherFamilyNamesInitialized) {
2103 continue;
2107 // load directly from the name table
2108 gfxFontEntry::AutoTable nameTable(fe, kNAME);
2109 if (!nameTable) {
2110 continue;
2113 if (aNeedFullnamePostscriptNames && !foundFaceNames) {
2114 if (gfxFontUtils::ReadCanonicalName(nameTable, gfxFontUtils::NAME_ID_FULL,
2115 fullname) == NS_OK) {
2116 aPlatformFontList->AddFullnameLocked(fe, fullname);
2119 if (gfxFontUtils::ReadCanonicalName(
2120 nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK) {
2121 aPlatformFontList->AddPostscriptNameLocked(fe, psname);
2125 if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
2126 bool foundOtherName =
2127 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
2129 // if the first face has a different name, scan all faces, otherwise
2130 // assume the family doesn't have other names
2131 if (firstTime && foundOtherName) {
2132 mHasOtherFamilyNames = true;
2133 readAllFaces = true;
2135 firstTime = false;
2138 // if not reading in any more names, skip other faces
2139 if (!readAllFaces && !aNeedFullnamePostscriptNames) {
2140 break;
2144 mFaceNamesInitialized = true;
2145 mOtherFamilyNamesInitialized = true;
2148 gfxFontEntry* gfxFontFamily::FindFont(const nsACString& aFontName,
2149 const nsCStringComparator& aCmp) const {
2150 // find the font using a simple linear search
2151 AutoReadLock lock(mLock);
2152 uint32_t numFonts = mAvailableFonts.Length();
2153 for (uint32_t i = 0; i < numFonts; i++) {
2154 gfxFontEntry* fe = mAvailableFonts[i].get();
2155 if (fe && fe->Name().Equals(aFontName, aCmp)) {
2156 return fe;
2159 return nullptr;
2162 void gfxFontFamily::ReadAllCMAPs(FontInfoData* aFontInfoData) {
2163 AutoWriteLock lock(mLock);
2164 FindStyleVariationsLocked(aFontInfoData);
2166 uint32_t i, numFonts = mAvailableFonts.Length();
2167 for (i = 0; i < numFonts; i++) {
2168 gfxFontEntry* fe = mAvailableFonts[i];
2169 // don't try to load cmaps for downloadable fonts not yet loaded
2170 if (!fe || fe->mIsUserFontContainer) {
2171 continue;
2173 fe->ReadCMAP(aFontInfoData);
2174 mFamilyCharacterMap.Union(*(fe->GetCharacterMap()));
2176 mFamilyCharacterMap.Compact();
2177 mFamilyCharacterMapInitialized = true;
2180 void gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2181 FontListSizes* aSizes) const {
2182 AutoReadLock lock(mLock);
2183 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2184 aSizes->mCharMapsSize +=
2185 mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
2187 aSizes->mFontListSize +=
2188 mAvailableFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
2189 for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
2190 gfxFontEntry* fe = mAvailableFonts[i];
2191 if (fe) {
2192 fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2197 void gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2198 FontListSizes* aSizes) const {
2199 aSizes->mFontListSize += aMallocSizeOf(this);
2200 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);