Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxFontEntry.cpp
blob8de9bfbb22d6f63fd80f38027ba3a02865ea901b
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() {
263 AutoReadLock lock(mLock);
264 if (mUnitsPerEm) {
265 return mUnitsPerEm;
269 AutoTable headTable(this, TRUETYPE_TAG('h', 'e', 'a', 'd'));
270 AutoWriteLock lock(mLock);
272 if (!mUnitsPerEm) {
273 if (headTable) {
274 uint32_t len;
275 const HeadTable* head =
276 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, &len));
277 if (len >= sizeof(HeadTable)) {
278 if (int16_t(head->xMax) > int16_t(head->xMin) &&
279 int16_t(head->yMax) > int16_t(head->yMin)) {
280 mXMin = head->xMin;
281 mYMin = head->yMin;
282 mXMax = head->xMax;
283 mYMax = head->yMax;
285 mUnitsPerEm = head->unitsPerEm;
289 // if we didn't find a usable 'head' table, or if the value was
290 // outside the valid range, record it as invalid
291 if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
292 mUnitsPerEm = kInvalidUPEM;
296 return mUnitsPerEm;
299 bool gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId) {
300 MOZ_ASSERT(mSVGInitialized,
301 "SVG data has not yet been loaded. TryGetSVGData() first.");
302 return GetSVGGlyphs()->HasSVGGlyph(aGlyphId);
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");
313 gfxMatrix svgToApp(aSize / mUnitsPerEm, 0, 0, aSize / mUnitsPerEm, 0, 0);
314 return GetSVGGlyphs()->GetGlyphExtents(aGlyphId, svgToApp, aResult);
317 void gfxFontEntry::RenderSVGGlyph(gfxContext* aContext, uint32_t aGlyphId,
318 SVGContextPaint* aContextPaint) {
319 MOZ_ASSERT(mSVGInitialized,
320 "SVG data has not yet been loaded. TryGetSVGData() first.");
321 GetSVGGlyphs()->RenderGlyph(aContext, aGlyphId, aContextPaint);
324 bool gfxFontEntry::TryGetSVGData(const gfxFont* aFont) {
325 if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
326 return false;
329 // We don't support SVG-in-OT glyphs in offscreen-canvas worker threads.
330 if (!NS_IsMainThread()) {
331 return false;
334 if (!mSVGInitialized) {
335 // If UnitsPerEm is not known/valid, we can't use SVG glyphs
336 if (UnitsPerEm() == kInvalidUPEM) {
337 mSVGInitialized = true;
338 return false;
341 // We don't use AutoTable here because we'll pass ownership of this
342 // blob to the gfxSVGGlyphs, once we've confirmed the table exists
343 hb_blob_t* svgTable = GetFontTable(TRUETYPE_TAG('S', 'V', 'G', ' '));
344 if (!svgTable) {
345 mSVGInitialized = true;
346 return false;
349 // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
350 // with it.
351 auto* svgGlyphs = new gfxSVGGlyphs(svgTable, this);
352 if (!mSVGGlyphs.compareExchange(nullptr, svgGlyphs)) {
353 delete svgGlyphs;
355 mSVGInitialized = true;
358 if (GetSVGGlyphs()) {
359 AutoWriteLock lock(mLock);
360 if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
361 mFontsUsingSVGGlyphs.AppendElement(aFont);
365 return !!GetSVGGlyphs();
368 void gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont) {
369 AutoWriteLock lock(mLock);
370 mFontsUsingSVGGlyphs.RemoveElement(aFont);
373 void gfxFontEntry::NotifyGlyphsChanged() {
374 AutoReadLock lock(mLock);
375 for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
376 const gfxFont* font = mFontsUsingSVGGlyphs[i];
377 font->NotifyGlyphsChanged();
381 bool gfxFontEntry::TryGetColorGlyphs() {
382 if (mCheckedForColorGlyph) {
383 return mCOLR && mCPAL;
386 auto* colr = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
387 auto* cpal = colr ? GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L')) : nullptr;
389 if (colr && cpal && gfx::COLRFonts::ValidateColorGlyphs(colr, cpal)) {
390 if (!mCOLR.compareExchange(nullptr, colr)) {
391 hb_blob_destroy(colr);
393 if (!mCPAL.compareExchange(nullptr, cpal)) {
394 hb_blob_destroy(cpal);
396 } else {
397 hb_blob_destroy(colr);
398 hb_blob_destroy(cpal);
401 mCheckedForColorGlyph = true;
402 return mCOLR && mCPAL;
406 * FontTableBlobData
408 * See FontTableHashEntry for the general strategy.
411 class gfxFontEntry::FontTableBlobData {
412 public:
413 explicit FontTableBlobData(nsTArray<uint8_t>&& aBuffer)
414 : mTableData(std::move(aBuffer)), mHashtable(nullptr), mHashKey(0) {
415 MOZ_COUNT_CTOR(FontTableBlobData);
418 ~FontTableBlobData() {
419 MOZ_COUNT_DTOR(FontTableBlobData);
420 if (mHashtable && mHashKey) {
421 mHashtable->RemoveEntry(mHashKey);
425 // Useful for creating blobs
426 const char* GetTable() const {
427 return reinterpret_cast<const char*>(mTableData.Elements());
429 uint32_t GetTableLength() const { return mTableData.Length(); }
431 // Tell this FontTableBlobData to remove the HashEntry when this is
432 // destroyed.
433 void ManageHashEntry(nsTHashtable<FontTableHashEntry>* aHashtable,
434 uint32_t aHashKey) {
435 mHashtable = aHashtable;
436 mHashKey = aHashKey;
439 // Disconnect from the HashEntry (because the blob has already been
440 // removed from the hashtable).
441 void ForgetHashEntry() {
442 mHashtable = nullptr;
443 mHashKey = 0;
446 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
447 return mTableData.ShallowSizeOfExcludingThis(aMallocSizeOf);
449 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
450 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
453 private:
454 // The font table data block
455 nsTArray<uint8_t> mTableData;
457 // The blob destroy function needs to know the owning hashtable
458 // and the hashtable key, so that it can remove the entry.
459 nsTHashtable<FontTableHashEntry>* mHashtable;
460 uint32_t mHashKey;
462 // not implemented
463 FontTableBlobData(const FontTableBlobData&);
466 hb_blob_t* gfxFontEntry::FontTableHashEntry::ShareTableAndGetBlob(
467 nsTArray<uint8_t>&& aTable, nsTHashtable<FontTableHashEntry>* aHashtable) {
468 Clear();
469 // adopts elements of aTable
470 mSharedBlobData = new FontTableBlobData(std::move(aTable));
472 mBlob = hb_blob_create(
473 mSharedBlobData->GetTable(), mSharedBlobData->GetTableLength(),
474 HB_MEMORY_MODE_READONLY, mSharedBlobData, DeleteFontTableBlobData);
475 if (mBlob == hb_blob_get_empty()) {
476 // The FontTableBlobData was destroyed during hb_blob_create().
477 // The (empty) blob is still be held in the hashtable with a strong
478 // reference.
479 return hb_blob_reference(mBlob);
482 // Tell the FontTableBlobData to remove this hash entry when destroyed.
483 // The hashtable does not keep a strong reference.
484 mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
485 return mBlob;
488 void gfxFontEntry::FontTableHashEntry::Clear() {
489 // If the FontTableBlobData is managing the hash entry, then the blob is
490 // not owned by this HashEntry; otherwise there is strong reference to the
491 // blob that must be removed.
492 if (mSharedBlobData) {
493 mSharedBlobData->ForgetHashEntry();
494 mSharedBlobData = nullptr;
495 } else {
496 hb_blob_destroy(mBlob);
498 mBlob = nullptr;
501 // a hb_destroy_func for hb_blob_create
503 /* static */
504 void gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(
505 void* aBlobData) {
506 delete static_cast<FontTableBlobData*>(aBlobData);
509 hb_blob_t* gfxFontEntry::FontTableHashEntry::GetBlob() const {
510 return hb_blob_reference(mBlob);
513 bool gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t** aBlob) {
514 // Accessing the mFontTableCache pointer is atomic, so we don't need to take
515 // a write lock even if we're initializing it here...
516 MOZ_PUSH_IGNORE_THREAD_SAFETY
517 if (MOZ_UNLIKELY(!mFontTableCache)) {
518 // We do this here rather than on fontEntry construction
519 // because not all shapers will access the table cache at all.
521 // We're not holding a write lock, so make sure to atomically update
522 // the cache pointer.
523 auto* newCache = new FontTableCache(8);
524 if (MOZ_UNLIKELY(!mFontTableCache.compareExchange(nullptr, newCache))) {
525 delete newCache;
528 FontTableCache* cache = GetFontTableCache();
529 MOZ_POP_THREAD_SAFETY
531 // ...but we do need a lock to read the actual hashtable contents.
532 AutoReadLock lock(mLock);
533 FontTableHashEntry* entry = cache->GetEntry(aTag);
534 if (!entry) {
535 return false;
538 *aBlob = entry->GetBlob();
539 return true;
542 hb_blob_t* gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
543 nsTArray<uint8_t>* aBuffer) {
544 MOZ_PUSH_IGNORE_THREAD_SAFETY
545 if (MOZ_UNLIKELY(!mFontTableCache)) {
546 auto* newCache = new FontTableCache(8);
547 if (MOZ_UNLIKELY(!mFontTableCache.compareExchange(nullptr, newCache))) {
548 delete newCache;
551 FontTableCache* cache = GetFontTableCache();
552 MOZ_POP_THREAD_SAFETY
554 AutoWriteLock lock(mLock);
555 FontTableHashEntry* entry = cache->PutEntry(aTag);
556 if (MOZ_UNLIKELY(!entry)) { // OOM
557 return nullptr;
560 if (!aBuffer) {
561 // ensure the entry is null
562 entry->Clear();
563 return nullptr;
566 return entry->ShareTableAndGetBlob(std::move(*aBuffer), cache);
569 already_AddRefed<gfxCharacterMap> gfxFontEntry::GetCMAPFromFontInfo(
570 FontInfoData* aFontInfoData, uint32_t& aUVSOffset) {
571 if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
572 return nullptr;
575 return aFontInfoData->GetCMAP(mName, aUVSOffset);
578 hb_blob_t* gfxFontEntry::GetFontTable(uint32_t aTag) {
579 hb_blob_t* blob;
580 if (GetExistingFontTable(aTag, &blob)) {
581 return blob;
584 nsTArray<uint8_t> buffer;
585 bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
587 return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
590 // callback for HarfBuzz to get a font table (in hb_blob_t form)
591 // from the font entry (passed as aUserData)
592 /*static*/
593 hb_blob_t* gfxFontEntry::HBGetTable(hb_face_t* face, uint32_t aTag,
594 void* aUserData) {
595 gfxFontEntry* fontEntry = static_cast<gfxFontEntry*>(aUserData);
597 // bug 589682 - ignore the GDEF table in buggy fonts (applies to
598 // Italic and BoldItalic faces of Times New Roman)
599 if (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') && fontEntry->IgnoreGDEF()) {
600 return nullptr;
603 // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
604 // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
605 if (aTag == TRUETYPE_TAG('G', 'S', 'U', 'B') && fontEntry->IgnoreGSUB()) {
606 return nullptr;
609 return fontEntry->GetFontTable(aTag);
612 static thread_local gfxFontEntry* tl_grGetFontTableCallbackData = nullptr;
614 class gfxFontEntryCallbacks {
615 public:
616 static tainted_gr<const void*> GrGetTable(
617 rlbox_sandbox_gr& sandbox, tainted_gr<const void*> /* aAppFaceHandle */,
618 tainted_gr<unsigned int> aName, tainted_gr<unsigned int*> aLen) {
619 gfxFontEntry* fontEntry = tl_grGetFontTableCallbackData;
620 *aLen = 0;
621 tainted_gr<const void*> ret = nullptr;
623 if (fontEntry) {
624 unsigned int fontTableKey = aName.unverified_safe_because(
625 "This is only being used to index into a hashmap, which is robust "
626 "for any value. No checks needed.");
627 gfxFontUtils::AutoHBBlob blob(fontEntry->GetFontTable(fontTableKey));
629 if (blob) {
630 unsigned int blobLength;
631 const void* tableData = hb_blob_get_data(blob, &blobLength);
632 // tableData is read-only data shared with the sandbox.
633 // Making a copy in sandbox memory
634 tainted_gr<void*> t_tableData = rlbox::sandbox_reinterpret_cast<void*>(
635 sandbox.malloc_in_sandbox<char>(blobLength));
636 if (t_tableData) {
637 rlbox::memcpy(sandbox, t_tableData, tableData, blobLength);
638 *aLen = blobLength;
639 ret = rlbox::sandbox_const_cast<const void*>(t_tableData);
644 return ret;
647 static void GrReleaseTable(rlbox_sandbox_gr& sandbox,
648 tainted_gr<const void*> /* aAppFaceHandle */,
649 tainted_gr<const void*> aTableBuffer) {
650 sandbox.free_in_sandbox(aTableBuffer);
653 static tainted_gr<float> GrGetAdvance(rlbox_sandbox_gr& sandbox,
654 tainted_gr<const void*> appFontHandle,
655 tainted_gr<uint16_t> glyphid) {
656 tainted_opaque_gr<float> ret = gfxGraphiteShaper::GrGetAdvance(
657 sandbox, appFontHandle.to_opaque(), glyphid.to_opaque());
658 return rlbox::from_opaque(ret);
662 struct gfxFontEntry::GrSandboxData {
663 rlbox_sandbox_gr sandbox;
664 sandbox_callback_gr<const void* (*)(const void*, unsigned int, unsigned int*)>
665 grGetTableCallback;
666 sandbox_callback_gr<void (*)(const void*, const void*)>
667 grReleaseTableCallback;
668 // Text Shapers register a callback to get glyph advances
669 sandbox_callback_gr<float (*)(const void*, uint16_t)>
670 grGetGlyphAdvanceCallback;
672 GrSandboxData() {
673 sandbox.create_sandbox();
674 grGetTableCallback =
675 sandbox.register_callback(gfxFontEntryCallbacks::GrGetTable);
676 grReleaseTableCallback =
677 sandbox.register_callback(gfxFontEntryCallbacks::GrReleaseTable);
678 grGetGlyphAdvanceCallback =
679 sandbox.register_callback(gfxFontEntryCallbacks::GrGetAdvance);
682 ~GrSandboxData() {
683 grGetTableCallback.unregister();
684 grReleaseTableCallback.unregister();
685 grGetGlyphAdvanceCallback.unregister();
686 sandbox.destroy_sandbox();
690 rlbox_sandbox_gr* gfxFontEntry::GetGrSandbox() {
691 AutoReadLock lock(mLock);
692 MOZ_ASSERT(mSandboxData != nullptr);
693 return &mSandboxData->sandbox;
696 sandbox_callback_gr<float (*)(const void*, uint16_t)>*
697 gfxFontEntry::GetGrSandboxAdvanceCallbackHandle() {
698 AutoReadLock lock(mLock);
699 MOZ_ASSERT(mSandboxData != nullptr);
700 return &mSandboxData->grGetGlyphAdvanceCallback;
703 tainted_opaque_gr<gr_face*> gfxFontEntry::GetGrFace() {
704 if (!mGrFaceInitialized) {
705 // When possible, the below code will use WASM as a sandboxing mechanism.
706 // At this time the wasm sandbox does not support threads.
707 // If Thebes is updated to make callst to the sandbox on multiple threaads,
708 // we need to make sure the underlying sandbox supports threading.
709 MOZ_ASSERT(NS_IsMainThread());
711 mSandboxData = new GrSandboxData();
713 auto p_faceOps = mSandboxData->sandbox.malloc_in_sandbox<gr_face_ops>();
714 if (!p_faceOps) {
715 MOZ_CRASH("Graphite sandbox memory allocation failed");
717 p_faceOps->size = sizeof(*p_faceOps);
718 p_faceOps->get_table = mSandboxData->grGetTableCallback;
719 p_faceOps->release_table = mSandboxData->grReleaseTableCallback;
721 tl_grGetFontTableCallbackData = this;
722 auto face = sandbox_invoke(
723 mSandboxData->sandbox, gr_make_face_with_ops,
724 // For security, we do not pass the callback data to this arg, and use
725 // a TLS var instead. However, gr_make_face_with_ops expects this to
726 // be a non null ptr. Therefore, we should pass some dummy non null
727 // pointer which will be passed to callbacks, but never used. Let's just
728 // pass p_faceOps again, as this is a non-null tainted pointer.
729 p_faceOps /* appFaceHandle */, p_faceOps, gr_face_default);
730 tl_grGetFontTableCallbackData = nullptr;
731 mGrFace = face.to_opaque();
732 mGrFaceInitialized = true;
733 mSandboxData->sandbox.free_in_sandbox(p_faceOps);
735 ++mGrFaceRefCnt;
736 return mGrFace;
739 void gfxFontEntry::ReleaseGrFace(tainted_opaque_gr<gr_face*> aFace) {
740 MOZ_ASSERT(
741 (rlbox::from_opaque(aFace) == rlbox::from_opaque(mGrFace))
742 .unverified_safe_because(
743 "This is safe as the only thing we are doing is comparing "
744 "addresses of two tainted pointers. Furthermore this is used "
745 "merely as a debugging aid in the debug builds. This function is "
746 "called only from the trusted Firefox code rather than the "
747 "untrusted libGraphite.")); // sanity-check
748 MOZ_ASSERT(mGrFaceRefCnt > 0);
749 if (--mGrFaceRefCnt == 0) {
750 auto t_mGrFace = rlbox::from_opaque(mGrFace);
752 tl_grGetFontTableCallbackData = this;
753 sandbox_invoke(mSandboxData->sandbox, gr_face_destroy, t_mGrFace);
754 tl_grGetFontTableCallbackData = nullptr;
756 t_mGrFace = nullptr;
757 mGrFace = t_mGrFace.to_opaque();
759 delete mSandboxData;
760 mSandboxData = nullptr;
762 mGrFaceInitialized = false;
766 void gfxFontEntry::DisconnectSVG() {
767 if (mSVGInitialized && mSVGGlyphs) {
768 mSVGGlyphs = nullptr;
769 mSVGInitialized = false;
773 bool gfxFontEntry::HasFontTable(uint32_t aTableTag) {
774 AutoTable table(this, aTableTag);
775 return table && hb_blob_get_length(table) > 0;
778 tainted_boolean_hint gfxFontEntry::HasGraphiteSpaceContextuals() {
779 LazyFlag flag = mHasGraphiteSpaceContextuals;
780 if (flag == LazyFlag::Uninitialized) {
781 auto face = GetGrFace();
782 auto t_face = rlbox::from_opaque(face);
783 if (t_face) {
784 tainted_gr<const gr_faceinfo*> faceInfo =
785 sandbox_invoke(mSandboxData->sandbox, gr_face_info, t_face, 0);
786 // Comparison with a value in sandboxed memory returns a
787 // tainted_boolean_hint, i.e. a "hint", since the value could be changed
788 // maliciously at any moment.
789 tainted_boolean_hint is_not_none =
790 faceInfo->space_contextuals != gr_faceinfo::gr_space_none;
791 flag = is_not_none.unverified_safe_because(
792 "Note ideally mHasGraphiteSpaceContextuals would be "
793 "tainted_boolean_hint, but RLBox does not yet support "
794 "bitfields, so it is not wrapped. However, its value is only "
795 "ever accessed through this function which returns a "
796 "tainted_boolean_hint, so unwrapping temporarily is safe. "
797 "We remove the wrapper now and re-add it below.")
798 ? LazyFlag::Yes
799 : LazyFlag::No;
801 ReleaseGrFace(face); // always balance GetGrFace, even if face is null
802 mHasGraphiteSpaceContextuals = flag;
805 return tainted_boolean_hint(flag == LazyFlag::Yes);
808 #define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag
810 static_assert(int(intl::Script::NUM_SCRIPT_CODES) <= FEATURE_SCRIPT_MASK,
811 "Too many script codes");
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)))
818 bool gfxFontEntry::SupportsOpenTypeFeature(Script aScript,
819 uint32_t aFeatureTag) {
820 MutexAutoLock lock(mFeatureInfoLock);
821 if (!mSupportedFeatures) {
822 mSupportedFeatures = MakeUnique<nsTHashMap<nsUint32HashKey, bool>>();
825 // note: high-order three bytes *must* be unique for each feature
826 // listed below (see SCRIPT_FEATURE macro def'n)
827 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'm', 'c', 'p') ||
828 aFeatureTag == HB_TAG('c', '2', 's', 'c') ||
829 aFeatureTag == HB_TAG('p', 'c', 'a', 'p') ||
830 aFeatureTag == HB_TAG('c', '2', 'p', 'c') ||
831 aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
832 aFeatureTag == HB_TAG('s', 'u', 'b', 's') ||
833 aFeatureTag == HB_TAG('v', 'e', 'r', 't'),
834 "use of unknown feature tag");
836 // note: graphite feature support uses the last script index
837 NS_ASSERTION(int(aScript) < FEATURE_SCRIPT_MASK - 1,
838 "need to bump the size of the feature shift");
840 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
841 return mSupportedFeatures->LookupOrInsertWith(scriptFeature, [&] {
842 bool result = false;
843 auto face(GetHBFace());
845 if (hb_ot_layout_has_substitution(face)) {
846 hb_script_t hbScript =
847 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
849 // Get the OpenType tag(s) that match this script code
850 unsigned int scriptCount = 4;
851 hb_tag_t scriptTags[4];
852 hb_ot_tags_from_script_and_language(hbScript, HB_LANGUAGE_INVALID,
853 &scriptCount, scriptTags, nullptr,
854 nullptr);
856 // Append DEFAULT to the returned tags, if room
857 if (scriptCount < 4) {
858 scriptTags[scriptCount++] = HB_OT_TAG_DEFAULT_SCRIPT;
861 // Now check for 'smcp' under the first of those scripts that is present
862 const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
863 result = std::any_of(scriptTags, scriptTags + scriptCount,
864 [&](const hb_tag_t& scriptTag) {
865 unsigned int scriptIndex;
866 return hb_ot_layout_table_find_script(
867 face, kGSUB, scriptTag, &scriptIndex) &&
868 hb_ot_layout_language_find_feature(
869 face, kGSUB, scriptIndex,
870 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
871 aFeatureTag, nullptr);
875 return result;
879 const hb_set_t* gfxFontEntry::InputsForOpenTypeFeature(Script aScript,
880 uint32_t aFeatureTag) {
881 MutexAutoLock lock(mFeatureInfoLock);
882 if (!mFeatureInputs) {
883 mFeatureInputs = MakeUnique<nsTHashMap<nsUint32HashKey, hb_set_t*>>();
886 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
887 aFeatureTag == HB_TAG('s', 'u', 'b', 's') ||
888 aFeatureTag == HB_TAG('v', 'e', 'r', 't'),
889 "use of unknown feature tag");
891 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
892 hb_set_t* inputGlyphs;
893 if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
894 return inputGlyphs;
897 inputGlyphs = hb_set_create();
899 auto face(GetHBFace());
901 if (hb_ot_layout_has_substitution(face)) {
902 hb_script_t hbScript =
903 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
905 // Get the OpenType tag(s) that match this script code
906 unsigned int scriptCount = 4;
907 hb_tag_t scriptTags[5]; // space for null terminator
908 hb_ot_tags_from_script_and_language(hbScript, HB_LANGUAGE_INVALID,
909 &scriptCount, scriptTags, nullptr,
910 nullptr);
912 // Append DEFAULT to the returned tags, if room
913 if (scriptCount < 4) {
914 scriptTags[scriptCount++] = HB_OT_TAG_DEFAULT_SCRIPT;
916 scriptTags[scriptCount++] = 0;
918 const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B');
919 hb_tag_t features[2] = {aFeatureTag, HB_TAG_NONE};
920 hb_set_t* featurelookups = hb_set_create();
921 hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr, features,
922 featurelookups);
923 hb_codepoint_t index = -1;
924 while (hb_set_next(featurelookups, &index)) {
925 hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index, nullptr,
926 inputGlyphs, nullptr, nullptr);
928 hb_set_destroy(featurelookups);
931 mFeatureInputs->InsertOrUpdate(scriptFeature, inputGlyphs);
932 return inputGlyphs;
935 bool gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag) {
936 MutexAutoLock lock(mFeatureInfoLock);
938 if (!mSupportedFeatures) {
939 mSupportedFeatures = MakeUnique<nsTHashMap<nsUint32HashKey, bool>>();
942 // note: high-order three bytes *must* be unique for each feature
943 // listed below (see SCRIPT_FEATURE macro def'n)
944 NS_ASSERTION(aFeatureTag == HB_TAG('s', 'm', 'c', 'p') ||
945 aFeatureTag == HB_TAG('c', '2', 's', 'c') ||
946 aFeatureTag == HB_TAG('p', 'c', 'a', 'p') ||
947 aFeatureTag == HB_TAG('c', '2', 'p', 'c') ||
948 aFeatureTag == HB_TAG('s', 'u', 'p', 's') ||
949 aFeatureTag == HB_TAG('s', 'u', 'b', 's'),
950 "use of unknown feature tag");
952 // graphite feature check uses the last script slot
953 uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag);
954 bool result;
955 if (mSupportedFeatures->Get(scriptFeature, &result)) {
956 return result;
959 auto face = GetGrFace();
960 auto t_face = rlbox::from_opaque(face);
961 result = t_face ? sandbox_invoke(mSandboxData->sandbox, gr_face_find_fref,
962 t_face, aFeatureTag) != nullptr
963 : false;
964 ReleaseGrFace(face);
966 mSupportedFeatures->InsertOrUpdate(scriptFeature, result);
968 return result;
971 void gfxFontEntry::GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo) {
972 // TODO: implement alternative code path for graphite fonts
974 auto autoFace(GetHBFace());
975 // Expose the raw hb_face_t to be captured by the lambdas (not the
976 // AutoHBFace wrapper).
977 hb_face_t* face = autoFace;
979 // Get the list of features for a specific <script,langSys> pair and
980 // append them to aFeatureInfo.
981 auto collectForLang = [=, &aFeatureInfo](
982 hb_tag_t aTableTag, unsigned int aScript,
983 hb_tag_t aScriptTag, unsigned int aLang,
984 hb_tag_t aLangTag) {
985 unsigned int featCount = hb_ot_layout_language_get_feature_tags(
986 face, aTableTag, aScript, aLang, 0, nullptr, nullptr);
987 AutoTArray<hb_tag_t, 32> featTags;
988 featTags.SetLength(featCount);
989 hb_ot_layout_language_get_feature_tags(face, aTableTag, aScript, aLang, 0,
990 &featCount, featTags.Elements());
991 MOZ_ASSERT(featCount <= featTags.Length());
992 // Just in case HB didn't fill featTags (i.e. in case it returned fewer
993 // tags than it promised), we truncate at the length it says it filled:
994 featTags.SetLength(featCount);
995 for (hb_tag_t t : featTags) {
996 aFeatureInfo.AppendElement(gfxFontFeatureInfo{t, aScriptTag, aLangTag});
1000 // Iterate over the language systems supported by a given script,
1001 // and call collectForLang for each of them.
1002 auto collectForScript = [=](hb_tag_t aTableTag, unsigned int aScript,
1003 hb_tag_t aScriptTag) {
1004 collectForLang(aTableTag, aScript, aScriptTag,
1005 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
1006 HB_TAG('d', 'f', 'l', 't'));
1007 unsigned int langCount = hb_ot_layout_script_get_language_tags(
1008 face, aTableTag, aScript, 0, nullptr, nullptr);
1009 AutoTArray<hb_tag_t, 32> langTags;
1010 langTags.SetLength(langCount);
1011 hb_ot_layout_script_get_language_tags(face, aTableTag, aScript, 0,
1012 &langCount, langTags.Elements());
1013 MOZ_ASSERT(langCount <= langTags.Length());
1014 langTags.SetLength(langCount);
1015 for (unsigned int lang = 0; lang < langCount; ++lang) {
1016 collectForLang(aTableTag, aScript, aScriptTag, lang, langTags[lang]);
1020 // Iterate over the scripts supported by a table (GSUB or GPOS), and call
1021 // collectForScript for each of them.
1022 auto collectForTable = [=](hb_tag_t aTableTag) {
1023 unsigned int scriptCount = hb_ot_layout_table_get_script_tags(
1024 face, aTableTag, 0, nullptr, nullptr);
1025 AutoTArray<hb_tag_t, 32> scriptTags;
1026 scriptTags.SetLength(scriptCount);
1027 hb_ot_layout_table_get_script_tags(face, aTableTag, 0, &scriptCount,
1028 scriptTags.Elements());
1029 MOZ_ASSERT(scriptCount <= scriptTags.Length());
1030 scriptTags.SetLength(scriptCount);
1031 for (unsigned int script = 0; script < scriptCount; ++script) {
1032 collectForScript(aTableTag, script, scriptTags[script]);
1036 // Collect all OpenType Layout features, both substitution and positioning,
1037 // supported by the font resource.
1038 collectForTable(HB_TAG('G', 'S', 'U', 'B'));
1039 collectForTable(HB_TAG('G', 'P', 'O', 'S'));
1042 typedef struct {
1043 AutoSwap_PRUint32 version;
1044 AutoSwap_PRUint16 format;
1045 AutoSwap_PRUint16 horizOffset;
1046 AutoSwap_PRUint16 vertOffset;
1047 AutoSwap_PRUint16 reserved;
1048 // TrackData horizData;
1049 // TrackData vertData;
1050 } TrakHeader;
1052 typedef struct {
1053 AutoSwap_PRUint16 nTracks;
1054 AutoSwap_PRUint16 nSizes;
1055 AutoSwap_PRUint32 sizeTableOffset;
1056 // trackTableEntry trackTable[];
1057 // fixed32 sizeTable[];
1058 } TrackData;
1060 typedef struct {
1061 AutoSwap_PRUint32 track;
1062 AutoSwap_PRUint16 nameIndex;
1063 AutoSwap_PRUint16 offset;
1064 } TrackTableEntry;
1066 bool gfxFontEntry::HasTrackingTable() {
1067 if (!TrakTableInitialized()) {
1068 hb_blob_t* trak = GetFontTable(TRUETYPE_TAG('t', 'r', 'a', 'k'));
1069 if (trak) {
1070 // mTrakTable itself is atomic, but we also want to set the auxiliary
1071 // pointers mTrakValues and mTrakSizeTable, so we take a lock here to
1072 // avoid racing with another thread also initializing the same values.
1073 AutoWriteLock lock(mLock);
1074 if (!mTrakTable.compareExchange(kTrakTableUninitialized, trak)) {
1075 hb_blob_destroy(trak);
1076 } else if (!ParseTrakTable()) {
1077 hb_blob_destroy(mTrakTable.exchange(nullptr));
1079 } else {
1080 mTrakTable.exchange(nullptr);
1083 return GetTrakTable() != nullptr;
1086 bool gfxFontEntry::ParseTrakTable() {
1087 // Check table validity and set up the subtable pointers we need;
1088 // if 'trak' table is invalid, or doesn't contain a 'normal' track,
1089 // return false to tell the caller not to try using it.
1090 unsigned int len;
1091 const char* data = hb_blob_get_data(GetTrakTable(), &len);
1092 if (len < sizeof(TrakHeader)) {
1093 return false;
1095 auto trak = reinterpret_cast<const TrakHeader*>(data);
1096 uint16_t horizOffset = trak->horizOffset;
1097 if (trak->version != 0x00010000 || uint16_t(trak->format) != 0 ||
1098 horizOffset == 0 || uint16_t(trak->reserved) != 0) {
1099 return false;
1101 // Find the horizontal trackData, and check it doesn't overrun the buffer.
1102 if (horizOffset > len - sizeof(TrackData)) {
1103 return false;
1105 auto trackData = reinterpret_cast<const TrackData*>(data + horizOffset);
1106 uint16_t nTracks = trackData->nTracks;
1107 mNumTrakSizes = trackData->nSizes;
1108 if (nTracks == 0 || mNumTrakSizes < 2) {
1109 return false;
1111 uint32_t sizeTableOffset = trackData->sizeTableOffset;
1112 // Find the trackTable, and check it doesn't overrun the buffer.
1113 if (horizOffset >
1114 len - (sizeof(TrackData) + nTracks * sizeof(TrackTableEntry))) {
1115 return false;
1117 auto trackTable = reinterpret_cast<const TrackTableEntry*>(
1118 data + horizOffset + sizeof(TrackData));
1119 // Look for 'normal' tracking, bail out if no such track is present.
1120 unsigned trackIndex;
1121 for (trackIndex = 0; trackIndex < nTracks; ++trackIndex) {
1122 if (trackTable[trackIndex].track == 0x00000000) {
1123 break;
1126 if (trackIndex == nTracks) {
1127 return false;
1129 // Find list of tracking values, and check they won't overrun.
1130 uint16_t offset = trackTable[trackIndex].offset;
1131 if (offset > len - mNumTrakSizes * sizeof(uint16_t)) {
1132 return false;
1134 mTrakValues = reinterpret_cast<const AutoSwap_PRInt16*>(data + offset);
1135 // Find the size subtable, and check it doesn't overrun the buffer.
1136 mTrakSizeTable =
1137 reinterpret_cast<const AutoSwap_PRInt32*>(data + sizeTableOffset);
1138 if (mTrakSizeTable + mNumTrakSizes >
1139 reinterpret_cast<const AutoSwap_PRInt32*>(data + len)) {
1140 return false;
1142 return true;
1145 gfxFloat gfxFontEntry::TrackingForCSSPx(gfxFloat aSize) const {
1146 // No locking because this does read-only access of fields that are inert
1147 // once initialized.
1148 MOZ_ASSERT(TrakTableInitialized() && mTrakTable && mTrakValues &&
1149 mTrakSizeTable);
1151 // Find index of first sizeTable entry that is >= the requested size.
1152 int32_t fixedSize = int32_t(aSize * 65536.0); // float -> 16.16 fixed-point
1153 unsigned sizeIndex;
1154 for (sizeIndex = 0; sizeIndex < mNumTrakSizes; ++sizeIndex) {
1155 if (mTrakSizeTable[sizeIndex] >= fixedSize) {
1156 break;
1159 // Return the tracking value for the requested size, or an interpolated
1160 // value if the exact size isn't found.
1161 if (sizeIndex == mNumTrakSizes) {
1162 // Request is larger than last entry in the table, so just use that.
1163 // (We don't attempt to extrapolate more extreme tracking values than
1164 // the largest or smallest present in the table.)
1165 return int16_t(mTrakValues[mNumTrakSizes - 1]);
1167 if (sizeIndex == 0 || mTrakSizeTable[sizeIndex] == fixedSize) {
1168 // Found an exact match, or size was smaller than the first entry.
1169 return int16_t(mTrakValues[sizeIndex]);
1171 // Requested size falls between two entries: interpolate value.
1172 double s0 = mTrakSizeTable[sizeIndex - 1] / 65536.0; // 16.16 -> float
1173 double s1 = mTrakSizeTable[sizeIndex] / 65536.0;
1174 double t = (aSize - s0) / (s1 - s0);
1175 return (1.0 - t) * int16_t(mTrakValues[sizeIndex - 1]) +
1176 t * int16_t(mTrakValues[sizeIndex]);
1179 void gfxFontEntry::SetupVariationRanges() {
1180 // No locking because this is done during initialization before any other
1181 // thread has access to the entry.
1182 if (!gfxPlatform::HasVariationFontSupport() ||
1183 !StaticPrefs::layout_css_font_variations_enabled() || !HasVariations() ||
1184 IsUserFont()) {
1185 return;
1187 AutoTArray<gfxFontVariationAxis, 4> axes;
1188 GetVariationAxes(axes);
1189 for (const auto& axis : axes) {
1190 switch (axis.mTag) {
1191 case HB_TAG('w', 'g', 'h', 't'):
1192 // If the axis range looks like it doesn't fit the CSS font-weight
1193 // scale, we don't hook up the high-level property, and we mark
1194 // the face (in mRangeFlags) as having non-standard weight. This
1195 // means we won't map CSS font-weight to the axis. Setting 'wght'
1196 // with font-variation-settings will still work.
1197 // Strictly speaking, the min value should be checked against 1.0,
1198 // not 0.0, but we'll allow font makers that amount of leeway, as
1199 // in practice a number of fonts seem to use 0..1000.
1200 if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
1201 // If axis.mMaxValue is less than the default weight we already
1202 // set up, assume the axis has a non-standard range (like Skia)
1203 // and don't try to map it.
1204 Weight().Min() <= FontWeight::FromFloat(axis.mMaxValue)) {
1205 if (FontWeight::FromFloat(axis.mDefaultValue) != Weight().Min()) {
1206 mStandardFace = false;
1208 mWeightRange =
1209 WeightRange(FontWeight::FromFloat(std::max(1.0f, axis.mMinValue)),
1210 FontWeight::FromFloat(axis.mMaxValue));
1211 } else {
1212 mRangeFlags |= RangeFlags::eNonCSSWeight;
1214 break;
1216 case HB_TAG('w', 'd', 't', 'h'):
1217 if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
1218 Stretch().Min() <= FontStretch::FromFloat(axis.mMaxValue)) {
1219 if (FontStretch::FromFloat(axis.mDefaultValue) != Stretch().Min()) {
1220 mStandardFace = false;
1222 mStretchRange = StretchRange(FontStretch::FromFloat(axis.mMinValue),
1223 FontStretch::FromFloat(axis.mMaxValue));
1224 } else {
1225 mRangeFlags |= RangeFlags::eNonCSSStretch;
1227 break;
1229 case HB_TAG('s', 'l', 'n', 't'):
1230 if (axis.mMinValue >= -90.0f && axis.mMaxValue <= 90.0f) {
1231 if (FontSlantStyle::FromFloat(axis.mDefaultValue) !=
1232 SlantStyle().Min()) {
1233 mStandardFace = false;
1235 // OpenType and CSS measure angles in opposite directions, so we
1236 // have to flip signs and swap min/max when setting up the CSS
1237 // font-style range here.
1238 mStyleRange =
1239 SlantStyleRange(FontSlantStyle::FromFloat(-axis.mMaxValue),
1240 FontSlantStyle::FromFloat(-axis.mMinValue));
1242 break;
1244 case HB_TAG('i', 't', 'a', 'l'):
1245 if (axis.mMinValue <= 0.0f && axis.mMaxValue >= 1.0f) {
1246 if (axis.mDefaultValue != 0.0f) {
1247 mStandardFace = false;
1249 mStyleRange =
1250 SlantStyleRange(FontSlantStyle::NORMAL, FontSlantStyle::ITALIC);
1252 break;
1254 default:
1255 continue;
1260 void gfxFontEntry::CheckForVariationAxes() {
1261 if (mCheckedForVariationAxes) {
1262 return;
1264 mCheckedForVariationAxes = true;
1265 if (HasVariations()) {
1266 AutoTArray<gfxFontVariationAxis, 4> axes;
1267 GetVariationAxes(axes);
1268 for (const auto& axis : axes) {
1269 if (axis.mTag == HB_TAG('w', 'g', 'h', 't') && axis.mMaxValue >= 600.0f) {
1270 mRangeFlags |= RangeFlags::eBoldVariableWeight;
1271 } else if (axis.mTag == HB_TAG('i', 't', 'a', 'l') &&
1272 axis.mMaxValue >= 1.0f) {
1273 mRangeFlags |= RangeFlags::eItalicVariation;
1274 } else if (axis.mTag == HB_TAG('s', 'l', 'n', 't')) {
1275 mRangeFlags |= RangeFlags::eSlantVariation;
1276 } else if (axis.mTag == HB_TAG('o', 'p', 's', 'z')) {
1277 mRangeFlags |= RangeFlags::eOpticalSize;
1283 bool gfxFontEntry::HasBoldVariableWeight() {
1284 MOZ_ASSERT(!mIsUserFontContainer,
1285 "should not be called for user-font containers!");
1286 CheckForVariationAxes();
1287 return bool(mRangeFlags & RangeFlags::eBoldVariableWeight);
1290 bool gfxFontEntry::HasItalicVariation() {
1291 MOZ_ASSERT(!mIsUserFontContainer,
1292 "should not be called for user-font containers!");
1293 CheckForVariationAxes();
1294 return bool(mRangeFlags & RangeFlags::eItalicVariation);
1297 bool gfxFontEntry::HasSlantVariation() {
1298 MOZ_ASSERT(!mIsUserFontContainer,
1299 "should not be called for user-font containers!");
1300 CheckForVariationAxes();
1301 return bool(mRangeFlags & RangeFlags::eSlantVariation);
1304 bool gfxFontEntry::HasOpticalSize() {
1305 MOZ_ASSERT(!mIsUserFontContainer,
1306 "should not be called for user-font containers!");
1307 CheckForVariationAxes();
1308 return bool(mRangeFlags & RangeFlags::eOpticalSize);
1311 void gfxFontEntry::GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
1312 const gfxFontStyle& aStyle) {
1313 if (!gfxPlatform::HasVariationFontSupport() ||
1314 !StaticPrefs::layout_css_font_variations_enabled()) {
1315 return;
1318 if (!HasVariations()) {
1319 return;
1322 // Resolve high-level CSS properties from the requested style
1323 // (font-{style,weight,stretch}) to the appropriate variations.
1324 // The value used is clamped to the range available in the font face,
1325 // unless the face is a user font where no explicit descriptor was
1326 // given, indicated by the corresponding 'auto' range-flag.
1328 // We don't do these mappings if the font entry has weight and/or stretch
1329 // ranges that do not appear to use the CSS property scale. Some older
1330 // fonts created for QuickDrawGX/AAT may use "normalized" values where the
1331 // standard variation is 1.0 rather than 400.0 (weight) or 100.0 (stretch).
1333 if (!(mRangeFlags & RangeFlags::eNonCSSWeight)) {
1334 float weight = (IsUserFont() && (mRangeFlags & RangeFlags::eAutoWeight))
1335 ? aStyle.weight.ToFloat()
1336 : Weight().Clamp(aStyle.weight).ToFloat();
1337 aResult.AppendElement(gfxFontVariation{HB_TAG('w', 'g', 'h', 't'), weight});
1340 if (!(mRangeFlags & RangeFlags::eNonCSSStretch)) {
1341 float stretch = (IsUserFont() && (mRangeFlags & RangeFlags::eAutoStretch))
1342 ? aStyle.stretch.ToFloat()
1343 : Stretch().Clamp(aStyle.stretch).ToFloat();
1344 aResult.AppendElement(
1345 gfxFontVariation{HB_TAG('w', 'd', 't', 'h'), stretch});
1348 if (aStyle.style.IsItalic() && SupportsItalic()) {
1349 // The 'ital' axis is normally a binary toggle; intermediate values
1350 // can only be set using font-variation-settings.
1351 aResult.AppendElement(gfxFontVariation{HB_TAG('i', 't', 'a', 'l'), 1.0f});
1352 } else if (aStyle.style != StyleFontStyle::NORMAL && HasSlantVariation()) {
1353 // Figure out what slant angle we should try to match from the
1354 // requested style.
1355 float angle = aStyle.style.SlantAngle();
1356 // Clamp to the available range, unless the face is a user font
1357 // with no explicit descriptor.
1358 if (!(IsUserFont() && (mRangeFlags & RangeFlags::eAutoSlantStyle))) {
1359 angle = SlantStyle().Clamp(FontSlantStyle::FromFloat(angle)).SlantAngle();
1361 // OpenType and CSS measure angles in opposite directions, so we have to
1362 // invert the sign of the CSS oblique value when setting OpenType 'slnt'.
1363 aResult.AppendElement(gfxFontVariation{HB_TAG('s', 'l', 'n', 't'), -angle});
1366 struct TagEquals {
1367 bool Equals(const gfxFontVariation& aIter, uint32_t aTag) const {
1368 return aIter.mTag == aTag;
1372 auto replaceOrAppend = [&aResult](const gfxFontVariation& aSetting) {
1373 auto index = aResult.IndexOf(aSetting.mTag, 0, TagEquals());
1374 if (index == aResult.NoIndex) {
1375 aResult.AppendElement(aSetting);
1376 } else {
1377 aResult[index].mValue = aSetting.mValue;
1381 // The low-level font-variation-settings descriptor from @font-face,
1382 // if present, takes precedence over automatic variation settings
1383 // from high-level properties.
1384 for (const auto& v : mVariationSettings) {
1385 replaceOrAppend(v);
1388 // And the low-level font-variation-settings property takes precedence
1389 // over the descriptor.
1390 for (const auto& v : aStyle.variationSettings) {
1391 replaceOrAppend(v);
1394 // If there's no explicit opsz in the settings, apply 'auto' value.
1395 if (HasOpticalSize() && aStyle.autoOpticalSize >= 0.0f) {
1396 const uint32_t kOpszTag = HB_TAG('o', 'p', 's', 'z');
1397 auto index = aResult.IndexOf(kOpszTag, 0, TagEquals());
1398 if (index == aResult.NoIndex) {
1399 float value = aStyle.autoOpticalSize * mSizeAdjust;
1400 aResult.AppendElement(gfxFontVariation{kOpszTag, value});
1405 size_t gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(
1406 mozilla::MallocSizeOf aMallocSizeOf) const {
1407 size_t n = 0;
1408 if (mBlob) {
1409 n += aMallocSizeOf(mBlob);
1411 if (mSharedBlobData) {
1412 n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
1414 return n;
1417 void gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1418 FontListSizes* aSizes) const {
1419 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1421 // cmaps are shared so only non-shared cmaps are included here
1422 if (mCharacterMap && GetCharacterMap()->mBuildOnTheFly) {
1423 aSizes->mCharMapsSize +=
1424 GetCharacterMap()->SizeOfIncludingThis(aMallocSizeOf);
1427 AutoReadLock lock(mLock);
1428 if (mFontTableCache) {
1429 aSizes->mFontTableCacheSize +=
1430 GetFontTableCache()->SizeOfIncludingThis(aMallocSizeOf);
1434 // If the font has UVS data, we count that as part of the character map.
1435 if (mUVSData) {
1436 aSizes->mCharMapsSize += aMallocSizeOf(GetUVSData());
1439 // The following, if present, are essentially cached forms of font table
1440 // data, so we'll accumulate them together with the basic table cache.
1441 if (mUserFontData) {
1442 aSizes->mFontTableCacheSize +=
1443 mUserFontData->SizeOfIncludingThis(aMallocSizeOf);
1445 if (mSVGGlyphs) {
1446 aSizes->mFontTableCacheSize +=
1447 GetSVGGlyphs()->SizeOfIncludingThis(aMallocSizeOf);
1451 MutexAutoLock lock(mFeatureInfoLock);
1452 if (mSupportedFeatures) {
1453 aSizes->mFontTableCacheSize +=
1454 mSupportedFeatures->ShallowSizeOfIncludingThis(aMallocSizeOf);
1456 if (mFeatureInputs) {
1457 aSizes->mFontTableCacheSize +=
1458 mFeatureInputs->ShallowSizeOfIncludingThis(aMallocSizeOf);
1459 // XXX Can't this simply be
1460 // aSizes->mFontTableCacheSize += 8192 * mFeatureInputs->Count();
1461 for (auto iter = mFeatureInputs->ConstIter(); !iter.Done(); iter.Next()) {
1462 // There's no API to get the real size of an hb_set, so we'll use
1463 // an approximation based on knowledge of the implementation.
1464 aSizes->mFontTableCacheSize += 8192; // vector of 64K bits
1468 // We don't include the size of mCOLR/mCPAL here, because (depending on the
1469 // font backend implementation) they will either wrap blocks of data owned
1470 // by the system (and potentially shared), or tables that are in our font
1471 // table cache and therefore already counted.
1474 void gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1475 FontListSizes* aSizes) const {
1476 aSizes->mFontListSize += aMallocSizeOf(this);
1477 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1480 // This is used to report the size of an individual downloaded font in the
1481 // user font cache. (Fonts that are part of the platform font list accumulate
1482 // their sizes to the font list's reporter using the AddSizeOf... methods
1483 // above.)
1484 size_t gfxFontEntry::ComputedSizeOfExcludingThis(
1485 MallocSizeOf aMallocSizeOf) const {
1486 FontListSizes s = {0};
1487 AddSizeOfExcludingThis(aMallocSizeOf, &s);
1489 // When reporting memory used for the main platform font list,
1490 // where we're typically summing the totals for a few hundred font faces,
1491 // we report the fields of FontListSizes separately.
1492 // But for downloaded user fonts, the actual resource data (added below)
1493 // will dominate, and the minor overhead of these pieces isn't worth
1494 // splitting out for an individual font.
1495 size_t result = s.mFontListSize + s.mFontTableCacheSize + s.mCharMapsSize;
1497 if (mIsDataUserFont) {
1498 MOZ_ASSERT(mComputedSizeOfUserFont > 0, "user font with no data?");
1499 result += mComputedSizeOfUserFont;
1502 return result;
1505 //////////////////////////////////////////////////////////////////////////////
1507 // class gfxFontFamily
1509 //////////////////////////////////////////////////////////////////////////////
1511 // we consider faces with mStandardFace == true to be "less than" those with
1512 // false, because during style matching, earlier entries are tried first
1513 class FontEntryStandardFaceComparator {
1514 public:
1515 bool Equals(const RefPtr<gfxFontEntry>& a,
1516 const RefPtr<gfxFontEntry>& b) const {
1517 return a->mStandardFace == b->mStandardFace;
1519 bool LessThan(const RefPtr<gfxFontEntry>& a,
1520 const RefPtr<gfxFontEntry>& b) const {
1521 return (a->mStandardFace == true && b->mStandardFace == false);
1525 void gfxFontFamily::SortAvailableFonts() {
1526 MOZ_ASSERT(mLock.LockedForWritingByCurrentThread());
1527 mAvailableFonts.Sort(FontEntryStandardFaceComparator());
1530 bool gfxFontFamily::HasOtherFamilyNames() {
1531 // need to read in other family names to determine this
1532 if (!mOtherFamilyNamesInitialized) {
1533 ReadOtherFamilyNames(
1534 gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
1536 return mHasOtherFamilyNames;
1539 gfxFontEntry* gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
1540 bool aIgnoreSizeTolerance) {
1541 AutoTArray<gfxFontEntry*, 4> matched;
1542 FindAllFontsForStyle(aFontStyle, matched, aIgnoreSizeTolerance);
1543 if (!matched.IsEmpty()) {
1544 return matched[0];
1546 return nullptr;
1549 static inline double WeightStyleStretchDistance(
1550 gfxFontEntry* aFontEntry, const gfxFontStyle& aTargetStyle) {
1551 double stretchDist =
1552 StretchDistance(aFontEntry->Stretch(), aTargetStyle.stretch);
1553 double styleDist =
1554 StyleDistance(aFontEntry->SlantStyle(), aTargetStyle.style);
1555 double weightDist = WeightDistance(aFontEntry->Weight(), aTargetStyle.weight);
1557 // Sanity-check that the distances are within the expected range
1558 // (update if implementation of the distance functions is changed).
1559 MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
1560 MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
1561 MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
1563 // weight/style/stretch priority: stretch >> style >> weight
1564 // so we multiply the stretch and style values to make them dominate
1565 // the result
1566 return stretchDist * kStretchFactor + styleDist * kStyleFactor +
1567 weightDist * kWeightFactor;
1570 void gfxFontFamily::FindAllFontsForStyle(
1571 const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
1572 bool aIgnoreSizeTolerance) {
1573 if (!mHasStyles) {
1574 FindStyleVariations(); // collect faces for the family, if not already
1575 // done
1578 AutoReadLock lock(mLock);
1580 NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
1581 NS_ASSERTION(aFontEntryList.IsEmpty(), "non-empty fontlist passed in");
1583 gfxFontEntry* fe = nullptr;
1585 // If the family has only one face, we simply return it; no further
1586 // checking needed
1587 uint32_t count = mAvailableFonts.Length();
1588 if (count == 1) {
1589 fe = mAvailableFonts[0];
1590 aFontEntryList.AppendElement(fe);
1591 return;
1594 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
1595 // or some subset of these. In this case, we have exactly 4 entries in
1596 // mAvailableFonts, stored in the above order; note that some of the entries
1597 // may be nullptr. We can then pick the required entry based on whether the
1598 // request is for bold or non-bold, italic or non-italic, without running the
1599 // more complex matching algorithm used for larger families with many weights
1600 // and/or widths.
1602 if (mIsSimpleFamily) {
1603 // Family has no more than the "standard" 4 faces, at fixed indexes;
1604 // calculate which one we want.
1605 // Note that we cannot simply return it as not all 4 faces are necessarily
1606 // present.
1607 bool wantBold = aFontStyle.weight >= FontWeight::FromInt(600);
1608 bool wantItalic = !aFontStyle.style.IsNormal();
1609 uint8_t faceIndex =
1610 (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
1612 // if the desired style is available, return it directly
1613 fe = mAvailableFonts[faceIndex];
1614 if (fe) {
1615 aFontEntryList.AppendElement(fe);
1616 return;
1619 // order to check fallback faces in a simple family, depending on requested
1620 // style
1621 static const uint8_t simpleFallbacks[4][3] = {
1622 {kBoldFaceIndex, kItalicFaceIndex,
1623 kBoldItalicFaceIndex}, // fallbacks for Regular
1624 {kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex}, // Bold
1625 {kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex}, // Italic
1626 {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex} // BoldItalic
1628 const uint8_t* order = simpleFallbacks[faceIndex];
1630 for (uint8_t trial = 0; trial < 3; ++trial) {
1631 // check remaining faces in order of preference to find the first that
1632 // actually exists
1633 fe = mAvailableFonts[order[trial]];
1634 if (fe) {
1635 aFontEntryList.AppendElement(fe);
1636 return;
1640 // this can't happen unless we have totally broken the font-list manager!
1641 MOZ_ASSERT_UNREACHABLE("no face found in simple font family!");
1644 // Pick the font(s) that are closest to the desired weight, style, and
1645 // stretch. Iterate over all fonts, measuring the weight/style distance.
1646 // Because of unicode-range values, there may be more than one font for a
1647 // given but the 99% use case is only a single font entry per
1648 // weight/style/stretch distance value. To optimize this, only add entries
1649 // to the matched font array when another entry already has the same
1650 // weight/style/stretch distance and add the last matched font entry. For
1651 // normal platform fonts with a single font entry for each
1652 // weight/style/stretch combination, only the last matched font entry will
1653 // be added.
1655 double minDistance = INFINITY;
1656 gfxFontEntry* matched = nullptr;
1657 // iterate in forward order so that faces like 'Bold' are matched before
1658 // matching style distance faces such as 'Bold Outline' (see bug 1185812)
1659 for (uint32_t i = 0; i < count; i++) {
1660 fe = mAvailableFonts[i];
1661 // weight/style/stretch priority: stretch >> style >> weight
1662 double distance = WeightStyleStretchDistance(fe, aFontStyle);
1663 if (distance < minDistance) {
1664 matched = fe;
1665 if (!aFontEntryList.IsEmpty()) {
1666 aFontEntryList.Clear();
1668 minDistance = distance;
1669 } else if (distance == minDistance) {
1670 if (matched) {
1671 aFontEntryList.AppendElement(matched);
1673 matched = fe;
1677 NS_ASSERTION(matched, "didn't match a font within a family");
1679 if (matched) {
1680 aFontEntryList.AppendElement(matched);
1684 void gfxFontFamily::CheckForSimpleFamily() {
1685 MOZ_ASSERT(mLock.LockedForWritingByCurrentThread());
1686 // already checked this family
1687 if (mIsSimpleFamily) {
1688 return;
1691 uint32_t count = mAvailableFonts.Length();
1692 if (count > 4 || count == 0) {
1693 return; // can't be "simple" if there are >4 faces;
1694 // if none then the family is unusable anyway
1697 if (count == 1) {
1698 mIsSimpleFamily = true;
1699 return;
1702 StretchRange firstStretch = mAvailableFonts[0]->Stretch();
1703 if (!firstStretch.IsSingle()) {
1704 return; // family with variation fonts is not considered "simple"
1707 gfxFontEntry* faces[4] = {0};
1708 for (uint8_t i = 0; i < count; ++i) {
1709 gfxFontEntry* fe = mAvailableFonts[i];
1710 if (fe->Stretch() != firstStretch || fe->IsOblique()) {
1711 // simple families don't have varying font-stretch or oblique
1712 return;
1714 if (!fe->Weight().IsSingle() || !fe->SlantStyle().IsSingle()) {
1715 return; // family with variation fonts is not considered "simple"
1717 uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
1718 (fe->SupportsBold() ? kBoldMask : 0);
1719 if (faces[faceIndex]) {
1720 return; // two faces resolve to the same slot; family isn't "simple"
1722 faces[faceIndex] = fe;
1725 // we have successfully slotted the available faces into the standard
1726 // 4-face framework
1727 mAvailableFonts.SetLength(4);
1728 for (uint8_t i = 0; i < 4; ++i) {
1729 if (mAvailableFonts[i].get() != faces[i]) {
1730 mAvailableFonts[i].swap(faces[i]);
1734 mIsSimpleFamily = true;
1737 #ifdef DEBUG
1738 bool gfxFontFamily::ContainsFace(gfxFontEntry* aFontEntry) {
1739 AutoReadLock lock(mLock);
1741 uint32_t i, numFonts = mAvailableFonts.Length();
1742 for (i = 0; i < numFonts; i++) {
1743 if (mAvailableFonts[i] == aFontEntry) {
1744 return true;
1746 // userfonts contain the actual real font entry
1747 if (mAvailableFonts[i] && mAvailableFonts[i]->mIsUserFontContainer) {
1748 gfxUserFontEntry* ufe =
1749 static_cast<gfxUserFontEntry*>(mAvailableFonts[i].get());
1750 if (ufe->GetPlatformFontEntry() == aFontEntry) {
1751 return true;
1755 return false;
1757 #endif
1759 void gfxFontFamily::LocalizedName(nsACString& aLocalizedName) {
1760 // just return the primary name; subclasses should override
1761 aLocalizedName = mName;
1764 void gfxFontFamily::FindFontForChar(GlobalFontMatch* aMatchData) {
1765 gfxPlatformFontList::PlatformFontList()->mLock.AssertCurrentThreadIn();
1768 AutoReadLock lock(mLock);
1769 if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
1770 // none of the faces in the family support the required char,
1771 // so bail out immediately
1772 return;
1776 nsCString charAndName;
1777 if (profiler_thread_is_being_profiled(
1778 Combine(ThreadProfilingFeatures::Sampling,
1779 ThreadProfilingFeatures::Markers))) {
1780 charAndName = nsPrintfCString("\\u%x %s", aMatchData->mCh, mName.get());
1782 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxFontFamily::FindFontForChar",
1783 LAYOUT, charAndName);
1785 AutoTArray<gfxFontEntry*, 4> entries;
1786 FindAllFontsForStyle(aMatchData->mStyle, entries,
1787 /*aIgnoreSizeTolerance*/ true);
1788 if (entries.IsEmpty()) {
1789 return;
1792 gfxFontEntry* fe = nullptr;
1793 float distance = INFINITY;
1795 for (auto e : entries) {
1796 if (e->SkipDuringSystemFallback()) {
1797 continue;
1800 aMatchData->mCmapsTested++;
1801 if (e->HasCharacter(aMatchData->mCh)) {
1802 aMatchData->mCount++;
1804 LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
1806 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
1807 intl::Script script =
1808 intl::UnicodeProperties::GetScriptCode(aMatchData->mCh);
1809 MOZ_LOG(log, LogLevel::Debug,
1810 ("(textrun-systemfallback-fonts) char: u+%6.6x "
1811 "script: %d match: [%s]\n",
1812 aMatchData->mCh, int(script), e->Name().get()));
1815 fe = e;
1816 distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
1817 if (aMatchData->mPresentation != eFontPresentation::Any) {
1818 RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
1819 if (!font) {
1820 continue;
1822 bool hasColorGlyph =
1823 font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
1824 if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
1825 distance += kPresentationMismatch;
1828 break;
1832 if (!fe && !aMatchData->mStyle.IsNormalStyle()) {
1833 // If style/weight/stretch was not Normal, see if we can
1834 // fall back to a next-best face (e.g. Arial Black -> Bold,
1835 // or Arial Narrow -> Regular).
1836 GlobalFontMatch data(aMatchData->mCh, aMatchData->mNextCh,
1837 aMatchData->mStyle, aMatchData->mPresentation);
1838 SearchAllFontsForChar(&data);
1839 if (!data.mBestMatch) {
1840 return;
1842 fe = data.mBestMatch;
1843 distance = data.mMatchDistance;
1846 if (!fe) {
1847 return;
1850 if (distance < aMatchData->mMatchDistance ||
1851 (distance == aMatchData->mMatchDistance &&
1852 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
1853 aMatchData->mBestMatch = fe;
1854 aMatchData->mMatchedFamily = this;
1855 aMatchData->mMatchDistance = distance;
1859 void gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch* aMatchData) {
1860 if (!mFamilyCharacterMapInitialized) {
1861 ReadAllCMAPs();
1863 AutoReadLock lock(mLock);
1864 if (!mFamilyCharacterMap.test(aMatchData->mCh)) {
1865 return;
1867 uint32_t i, numFonts = mAvailableFonts.Length();
1868 for (i = 0; i < numFonts; i++) {
1869 gfxFontEntry* fe = mAvailableFonts[i];
1870 if (fe && fe->HasCharacter(aMatchData->mCh)) {
1871 float distance = WeightStyleStretchDistance(fe, aMatchData->mStyle);
1872 if (aMatchData->mPresentation != eFontPresentation::Any) {
1873 RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
1874 if (!font) {
1875 continue;
1877 bool hasColorGlyph =
1878 font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
1879 if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
1880 distance += kPresentationMismatch;
1883 if (distance < aMatchData->mMatchDistance ||
1884 (distance == aMatchData->mMatchDistance &&
1885 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) {
1886 aMatchData->mBestMatch = fe;
1887 aMatchData->mMatchedFamily = this;
1888 aMatchData->mMatchDistance = distance;
1894 /*virtual*/
1895 gfxFontFamily::~gfxFontFamily() {
1896 // Should not be dropped by stylo, but the InitFontList thread might use
1897 // a transient gfxFontFamily and that's OK.
1898 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
1901 // returns true if other names were found, false otherwise
1902 bool gfxFontFamily::ReadOtherFamilyNamesForFace(
1903 gfxPlatformFontList* aPlatformFontList, hb_blob_t* aNameTable,
1904 bool useFullName) {
1905 uint32_t dataLength;
1906 const char* nameData = hb_blob_get_data(aNameTable, &dataLength);
1907 AutoTArray<nsCString, 4> otherFamilyNames;
1909 gfxFontUtils::ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
1910 otherFamilyNames, useFullName);
1912 if (!otherFamilyNames.IsEmpty()) {
1913 aPlatformFontList->AddOtherFamilyNames(this, otherFamilyNames);
1916 return !otherFamilyNames.IsEmpty();
1919 void gfxFontFamily::ReadOtherFamilyNames(
1920 gfxPlatformFontList* aPlatformFontList) {
1921 AutoWriteLock lock(mLock);
1922 if (mOtherFamilyNamesInitialized) {
1923 return;
1926 mOtherFamilyNamesInitialized = true;
1928 FindStyleVariationsLocked();
1930 // read in other family names for the first face in the list
1931 uint32_t i, numFonts = mAvailableFonts.Length();
1932 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
1934 for (i = 0; i < numFonts; ++i) {
1935 gfxFontEntry* fe = mAvailableFonts[i];
1936 if (!fe) {
1937 continue;
1939 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1940 if (!nameTable) {
1941 continue;
1943 mHasOtherFamilyNames =
1944 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1945 break;
1948 // read in other names for the first face in the list with the assumption
1949 // that if extra names don't exist in that face then they don't exist in
1950 // other faces for the same font
1951 if (!mHasOtherFamilyNames) {
1952 return;
1955 // read in names for all faces, needed to catch cases where fonts have
1956 // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
1957 for (; i < numFonts; i++) {
1958 gfxFontEntry* fe = mAvailableFonts[i];
1959 if (!fe) {
1960 continue;
1962 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1963 if (!nameTable) {
1964 continue;
1966 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1970 static bool LookForLegacyFamilyName(const nsACString& aCanonicalName,
1971 const char* aNameData, uint32_t aDataLength,
1972 nsACString& aLegacyName /* outparam */) {
1973 const gfxFontUtils::NameHeader* nameHeader =
1974 reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
1976 uint32_t nameCount = nameHeader->count;
1977 if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
1978 NS_WARNING("invalid font (name records)");
1979 return false;
1982 const gfxFontUtils::NameRecord* nameRecord =
1983 reinterpret_cast<const gfxFontUtils::NameRecord*>(
1984 aNameData + sizeof(gfxFontUtils::NameHeader));
1985 uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
1987 for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
1988 uint32_t nameLen = nameRecord->length;
1989 uint32_t nameOff = nameRecord->offset;
1991 if (stringsBase + nameOff + nameLen > aDataLength) {
1992 NS_WARNING("invalid font (name table strings)");
1993 return false;
1996 if (uint16_t(nameRecord->nameID) == gfxFontUtils::NAME_ID_FAMILY) {
1997 bool ok = gfxFontUtils::DecodeFontName(
1998 aNameData + stringsBase + nameOff, nameLen,
1999 uint32_t(nameRecord->platformID), uint32_t(nameRecord->encodingID),
2000 uint32_t(nameRecord->languageID), aLegacyName);
2001 // It's only a legacy name if it case-insensitively differs from the
2002 // canonical name (otherwise it would map to the same key).
2003 if (ok && !aLegacyName.Equals(aCanonicalName,
2004 nsCaseInsensitiveCStringComparator)) {
2005 return true;
2009 return false;
2012 bool gfxFontFamily::CheckForLegacyFamilyNames(gfxPlatformFontList* aFontList) {
2013 aFontList->mLock.AssertCurrentThreadIn();
2014 if (mCheckedForLegacyFamilyNames) {
2015 // we already did this, so there's nothing more to add
2016 return false;
2018 mCheckedForLegacyFamilyNames = true;
2019 bool added = false;
2020 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
2021 AutoTArray<RefPtr<gfxFontEntry>, 16> faces;
2023 // Take a local copy of the array of font entries, because it's possible
2024 // AddWithLegacyFamilyName will mutate it (and it needs to be able to take
2025 // an exclusive lock on the family to do so, so we release the read lock
2026 // here).
2027 AutoReadLock lock(mLock);
2028 faces.AppendElements(mAvailableFonts);
2030 for (const auto& fe : faces) {
2031 if (!fe) {
2032 continue;
2034 gfxFontEntry::AutoTable nameTable(fe, kNAME);
2035 if (!nameTable) {
2036 continue;
2038 nsAutoCString legacyName;
2039 uint32_t dataLength;
2040 const char* nameData = hb_blob_get_data(nameTable, &dataLength);
2041 if (LookForLegacyFamilyName(Name(), nameData, dataLength, legacyName)) {
2042 if (aFontList->AddWithLegacyFamilyName(legacyName, fe, mVisibility)) {
2043 added = true;
2047 return added;
2050 void gfxFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
2051 bool aNeedFullnamePostscriptNames,
2052 FontInfoData* aFontInfoData) {
2053 aPlatformFontList->mLock.AssertCurrentThreadIn();
2055 // if all needed names have already been read, skip
2056 if (mOtherFamilyNamesInitialized &&
2057 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2058 return;
2061 AutoWriteLock lock(mLock);
2063 bool asyncFontLoaderDisabled = false;
2065 if (!mOtherFamilyNamesInitialized && aFontInfoData &&
2066 aFontInfoData->mLoadOtherNames && !asyncFontLoaderDisabled) {
2067 const auto* otherFamilyNames = aFontInfoData->GetOtherFamilyNames(mName);
2068 if (otherFamilyNames && otherFamilyNames->Length()) {
2069 aPlatformFontList->AddOtherFamilyNames(this, *otherFamilyNames);
2071 mOtherFamilyNamesInitialized = true;
2074 // if all needed data has been initialized, return
2075 if (mOtherFamilyNamesInitialized &&
2076 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2077 return;
2080 FindStyleVariationsLocked(aFontInfoData);
2082 // check again, as style enumeration code may have loaded names
2083 if (mOtherFamilyNamesInitialized &&
2084 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
2085 return;
2088 uint32_t i, numFonts = mAvailableFonts.Length();
2089 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
2091 bool firstTime = true, readAllFaces = false;
2092 for (i = 0; i < numFonts; ++i) {
2093 gfxFontEntry* fe = mAvailableFonts[i];
2094 if (!fe) {
2095 continue;
2098 nsAutoCString fullname, psname;
2099 bool foundFaceNames = false;
2100 if (!mFaceNamesInitialized && aNeedFullnamePostscriptNames &&
2101 aFontInfoData && aFontInfoData->mLoadFaceNames) {
2102 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
2103 if (!fullname.IsEmpty()) {
2104 aPlatformFontList->AddFullnameLocked(fe, fullname);
2106 if (!psname.IsEmpty()) {
2107 aPlatformFontList->AddPostscriptNameLocked(fe, psname);
2109 foundFaceNames = true;
2111 // found everything needed? skip to next font
2112 if (mOtherFamilyNamesInitialized) {
2113 continue;
2117 // load directly from the name table
2118 gfxFontEntry::AutoTable nameTable(fe, kNAME);
2119 if (!nameTable) {
2120 continue;
2123 if (aNeedFullnamePostscriptNames && !foundFaceNames) {
2124 if (gfxFontUtils::ReadCanonicalName(nameTable, gfxFontUtils::NAME_ID_FULL,
2125 fullname) == NS_OK) {
2126 aPlatformFontList->AddFullnameLocked(fe, fullname);
2129 if (gfxFontUtils::ReadCanonicalName(
2130 nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK) {
2131 aPlatformFontList->AddPostscriptNameLocked(fe, psname);
2135 if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
2136 bool foundOtherName =
2137 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
2139 // if the first face has a different name, scan all faces, otherwise
2140 // assume the family doesn't have other names
2141 if (firstTime && foundOtherName) {
2142 mHasOtherFamilyNames = true;
2143 readAllFaces = true;
2145 firstTime = false;
2148 // if not reading in any more names, skip other faces
2149 if (!readAllFaces && !aNeedFullnamePostscriptNames) {
2150 break;
2154 mFaceNamesInitialized = true;
2155 mOtherFamilyNamesInitialized = true;
2158 gfxFontEntry* gfxFontFamily::FindFont(const nsACString& aFontName,
2159 const nsCStringComparator& aCmp) const {
2160 // find the font using a simple linear search
2161 AutoReadLock lock(mLock);
2162 uint32_t numFonts = mAvailableFonts.Length();
2163 for (uint32_t i = 0; i < numFonts; i++) {
2164 gfxFontEntry* fe = mAvailableFonts[i].get();
2165 if (fe && fe->Name().Equals(aFontName, aCmp)) {
2166 return fe;
2169 return nullptr;
2172 void gfxFontFamily::ReadAllCMAPs(FontInfoData* aFontInfoData) {
2173 AutoWriteLock lock(mLock);
2174 FindStyleVariationsLocked(aFontInfoData);
2176 uint32_t i, numFonts = mAvailableFonts.Length();
2177 for (i = 0; i < numFonts; i++) {
2178 gfxFontEntry* fe = mAvailableFonts[i];
2179 // don't try to load cmaps for downloadable fonts not yet loaded
2180 if (!fe || fe->mIsUserFontContainer) {
2181 continue;
2183 fe->ReadCMAP(aFontInfoData);
2184 mFamilyCharacterMap.Union(*(fe->GetCharacterMap()));
2186 mFamilyCharacterMap.Compact();
2187 mFamilyCharacterMapInitialized = true;
2190 void gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2191 FontListSizes* aSizes) const {
2192 AutoReadLock lock(mLock);
2193 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2194 aSizes->mCharMapsSize +=
2195 mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
2197 aSizes->mFontListSize +=
2198 mAvailableFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
2199 for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
2200 gfxFontEntry* fe = mAvailableFonts[i];
2201 if (fe) {
2202 fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
2207 void gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2208 FontListSizes* aSizes) const {
2209 aSizes->mFontListSize += aMallocSizeOf(this);
2210 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);