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"
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"
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
)
62 mLock("gfxFontEntry lock"),
63 mFeatureInfoLock("gfxFontEntry featureInfo mutex"),
65 mIsBadUnderlineFont(false),
66 mIsUserFontContainer(false),
67 mIsDataUserFont(false),
68 mIsLocalUserFont(false),
69 mStandardFace(aIsStandardFace
),
72 mSkipDefaultFeatureSpaceCheck(false),
73 mSVGInitialized(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();
114 delete mFontTableCache
.exchange(nullptr);
115 delete mSVGGlyphs
.exchange(nullptr);
116 delete[] mUVSData
.exchange(nullptr);
118 gfxCharacterMap
* cmap
= mCharacterMap
.exchange(nullptr);
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
) {
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
) {
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
) {
167 NS_ASSERTION(mCharacterMap
|| mShmemCharacterMap
,
168 "failed to initialize character map");
176 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
177 const uint32_t kCmapTag
= TRUETYPE_TAG('c', 'm', 'a', 'p');
178 AutoTable
cmapTable(this, kCmapTag
);
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
)) {
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
);
207 bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t
* aScriptTags
,
209 auto face(GetHBFace());
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
,
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
229 nsCString
gfxFontEntry::RealFaceName() {
230 AutoTable
nameTable(this, TRUETYPE_TAG('n', 'a', 'm', 'e'));
233 nsresult rv
= gfxFontUtils::GetFullNameFromTable(nameTable
, name
);
234 if (NS_SUCCEEDED(rv
)) {
235 return std::move(name
);
241 already_AddRefed
<gfxFont
> gfxFontEntry::FindOrMakeFont(
242 const gfxFontStyle
* aStyle
, gfxCharacterMap
* aUnicodeRangeMap
) {
243 RefPtr
<gfxFont
> font
=
244 gfxFontCache::GetCache()->Lookup(this, aStyle
, aUnicodeRangeMap
);
246 return font
.forget();
249 gfxFont
* newFont
= CreateFontInstance(aStyle
);
253 if (!newFont
->Valid()) {
257 newFont
->SetUnicodeRangeMap(aUnicodeRangeMap
);
258 return gfxFontCache::GetCache()->MaybeInsert(newFont
);
261 uint16_t gfxFontEntry::UnitsPerEm() {
263 AutoReadLock
lock(mLock
);
269 AutoTable
headTable(this, TRUETYPE_TAG('h', 'e', 'a', 'd'));
270 AutoWriteLock
lock(mLock
);
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
)) {
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
;
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
,
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()) {
329 // We don't support SVG-in-OT glyphs in offscreen-canvas worker threads.
330 if (!NS_IsMainThread()) {
334 if (!mSVGInitialized
) {
335 // If UnitsPerEm is not known/valid, we can't use SVG glyphs
336 if (UnitsPerEm() == kInvalidUPEM
) {
337 mSVGInitialized
= true;
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', ' '));
345 mSVGInitialized
= true;
349 // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
351 auto* svgGlyphs
= new gfxSVGGlyphs(svgTable
, this);
352 if (!mSVGGlyphs
.compareExchange(nullptr, 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
);
397 hb_blob_destroy(colr
);
398 hb_blob_destroy(cpal
);
401 mCheckedForColorGlyph
= true;
402 return mCOLR
&& mCPAL
;
408 * See FontTableHashEntry for the general strategy.
411 class gfxFontEntry::FontTableBlobData
{
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
433 void ManageHashEntry(nsTHashtable
<FontTableHashEntry
>* aHashtable
,
435 mHashtable
= aHashtable
;
439 // Disconnect from the HashEntry (because the blob has already been
440 // removed from the hashtable).
441 void ForgetHashEntry() {
442 mHashtable
= nullptr;
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
);
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
;
463 FontTableBlobData(const FontTableBlobData
&);
466 hb_blob_t
* gfxFontEntry::FontTableHashEntry::ShareTableAndGetBlob(
467 nsTArray
<uint8_t>&& aTable
, nsTHashtable
<FontTableHashEntry
>* aHashtable
) {
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
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());
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;
496 hb_blob_destroy(mBlob
);
501 // a hb_destroy_func for hb_blob_create
504 void gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(
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
))) {
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
);
538 *aBlob
= entry
->GetBlob();
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
))) {
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
561 // ensure the entry is null
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
) {
575 return aFontInfoData
->GetCMAP(mName
, aUVSOffset
);
578 hb_blob_t
* gfxFontEntry::GetFontTable(uint32_t aTag
) {
580 if (GetExistingFontTable(aTag
, &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)
593 hb_blob_t
* gfxFontEntry::HBGetTable(hb_face_t
* face
, uint32_t aTag
,
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()) {
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()) {
609 return fontEntry
->GetFontTable(aTag
);
612 static thread_local gfxFontEntry
* tl_grGetFontTableCallbackData
= nullptr;
614 class gfxFontEntryCallbacks
{
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
;
621 tainted_gr
<const void*> ret
= nullptr;
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
));
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
));
637 rlbox::memcpy(sandbox
, t_tableData
, tableData
, blobLength
);
639 ret
= rlbox::sandbox_const_cast
<const void*>(t_tableData
);
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*)>
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
;
673 sandbox
.create_sandbox();
675 sandbox
.register_callback(gfxFontEntryCallbacks::GrGetTable
);
676 grReleaseTableCallback
=
677 sandbox
.register_callback(gfxFontEntryCallbacks::GrReleaseTable
);
678 grGetGlyphAdvanceCallback
=
679 sandbox
.register_callback(gfxFontEntryCallbacks::GrGetAdvance
);
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
>();
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
);
739 void gfxFontEntry::ReleaseGrFace(tainted_opaque_gr
<gr_face
*> aFace
) {
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;
757 mGrFace
= t_mGrFace
.to_opaque();
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
);
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.")
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
, [&] {
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,
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);
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
)) {
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,
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
,
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
);
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
);
955 if (mSupportedFeatures
->Get(scriptFeature
, &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
966 mSupportedFeatures
->InsertOrUpdate(scriptFeature
, 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
,
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'));
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;
1053 AutoSwap_PRUint16 nTracks
;
1054 AutoSwap_PRUint16 nSizes
;
1055 AutoSwap_PRUint32 sizeTableOffset
;
1056 // trackTableEntry trackTable[];
1057 // fixed32 sizeTable[];
1061 AutoSwap_PRUint32 track
;
1062 AutoSwap_PRUint16 nameIndex
;
1063 AutoSwap_PRUint16 offset
;
1066 bool gfxFontEntry::HasTrackingTable() {
1067 if (!TrakTableInitialized()) {
1068 hb_blob_t
* trak
= GetFontTable(TRUETYPE_TAG('t', 'r', 'a', 'k'));
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));
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.
1091 const char* data
= hb_blob_get_data(GetTrakTable(), &len
);
1092 if (len
< sizeof(TrakHeader
)) {
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) {
1101 // Find the horizontal trackData, and check it doesn't overrun the buffer.
1102 if (horizOffset
> len
- sizeof(TrackData
)) {
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) {
1111 uint32_t sizeTableOffset
= trackData
->sizeTableOffset
;
1112 // Find the trackTable, and check it doesn't overrun the buffer.
1114 len
- (sizeof(TrackData
) + nTracks
* sizeof(TrackTableEntry
))) {
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) {
1126 if (trackIndex
== nTracks
) {
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)) {
1134 mTrakValues
= reinterpret_cast<const AutoSwap_PRInt16
*>(data
+ offset
);
1135 // Find the size subtable, and check it doesn't overrun the buffer.
1137 reinterpret_cast<const AutoSwap_PRInt32
*>(data
+ sizeTableOffset
);
1138 if (mTrakSizeTable
+ mNumTrakSizes
>
1139 reinterpret_cast<const AutoSwap_PRInt32
*>(data
+ len
)) {
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
&&
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
1154 for (sizeIndex
= 0; sizeIndex
< mNumTrakSizes
; ++sizeIndex
) {
1155 if (mTrakSizeTable
[sizeIndex
] >= fixedSize
) {
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() ||
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;
1209 WeightRange(FontWeight::FromFloat(std::max(1.0f
, axis
.mMinValue
)),
1210 FontWeight::FromFloat(axis
.mMaxValue
));
1212 mRangeFlags
|= RangeFlags::eNonCSSWeight
;
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
));
1225 mRangeFlags
|= RangeFlags::eNonCSSStretch
;
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.
1239 SlantStyleRange(FontSlantStyle::FromFloat(-axis
.mMaxValue
),
1240 FontSlantStyle::FromFloat(-axis
.mMinValue
));
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;
1250 SlantStyleRange(FontSlantStyle::NORMAL
, FontSlantStyle::ITALIC
);
1260 void gfxFontEntry::CheckForVariationAxes() {
1261 if (mCheckedForVariationAxes
) {
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()) {
1318 if (!HasVariations()) {
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
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
});
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
);
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
) {
1388 // And the low-level font-variation-settings property takes precedence
1389 // over the descriptor.
1390 for (const auto& v
: aStyle
.variationSettings
) {
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 {
1409 n
+= aMallocSizeOf(mBlob
);
1411 if (mSharedBlobData
) {
1412 n
+= mSharedBlobData
->SizeOfIncludingThis(aMallocSizeOf
);
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.
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
);
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
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
;
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
{
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()) {
1549 static inline double WeightStyleStretchDistance(
1550 gfxFontEntry
* aFontEntry
, const gfxFontStyle
& aTargetStyle
) {
1551 double stretchDist
=
1552 StretchDistance(aFontEntry
->Stretch(), aTargetStyle
.stretch
);
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
1566 return stretchDist
* kStretchFactor
+ styleDist
* kStyleFactor
+
1567 weightDist
* kWeightFactor
;
1570 void gfxFontFamily::FindAllFontsForStyle(
1571 const gfxFontStyle
& aFontStyle
, nsTArray
<gfxFontEntry
*>& aFontEntryList
,
1572 bool aIgnoreSizeTolerance
) {
1574 FindStyleVariations(); // collect faces for the family, if not already
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
1587 uint32_t count
= mAvailableFonts
.Length();
1589 fe
= mAvailableFonts
[0];
1590 aFontEntryList
.AppendElement(fe
);
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
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
1607 bool wantBold
= aFontStyle
.weight
>= FontWeight::FromInt(600);
1608 bool wantItalic
= !aFontStyle
.style
.IsNormal();
1610 (wantItalic
? kItalicMask
: 0) | (wantBold
? kBoldMask
: 0);
1612 // if the desired style is available, return it directly
1613 fe
= mAvailableFonts
[faceIndex
];
1615 aFontEntryList
.AppendElement(fe
);
1619 // order to check fallback faces in a simple family, depending on requested
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
1633 fe
= mAvailableFonts
[order
[trial
]];
1635 aFontEntryList
.AppendElement(fe
);
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
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
) {
1665 if (!aFontEntryList
.IsEmpty()) {
1666 aFontEntryList
.Clear();
1668 minDistance
= distance
;
1669 } else if (distance
== minDistance
) {
1671 aFontEntryList
.AppendElement(matched
);
1677 NS_ASSERTION(matched
, "didn't match a font within a family");
1680 aFontEntryList
.AppendElement(matched
);
1684 void gfxFontFamily::CheckForSimpleFamily() {
1685 MOZ_ASSERT(mLock
.LockedForWritingByCurrentThread());
1686 // already checked this family
1687 if (mIsSimpleFamily
) {
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
1698 mIsSimpleFamily
= true;
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
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
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;
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
) {
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
) {
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
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()) {
1792 gfxFontEntry
* fe
= nullptr;
1793 float distance
= INFINITY
;
1795 for (auto e
: entries
) {
1796 if (e
->SkipDuringSystemFallback()) {
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()));
1816 distance
= WeightStyleStretchDistance(fe
, aMatchData
->mStyle
);
1817 if (aMatchData
->mPresentation
!= eFontPresentation::Any
) {
1818 RefPtr
<gfxFont
> font
= fe
->FindOrMakeFont(&aMatchData
->mStyle
);
1822 bool hasColorGlyph
=
1823 font
->HasColorGlyphFor(aMatchData
->mCh
, aMatchData
->mNextCh
);
1824 if (hasColorGlyph
!= PrefersColor(aMatchData
->mPresentation
)) {
1825 distance
+= kPresentationMismatch
;
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
) {
1842 fe
= data
.mBestMatch
;
1843 distance
= data
.mMatchDistance
;
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
) {
1863 AutoReadLock
lock(mLock
);
1864 if (!mFamilyCharacterMap
.test(aMatchData
->mCh
)) {
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
);
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
;
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
,
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
) {
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
];
1939 gfxFontEntry::AutoTable
nameTable(fe
, kNAME
);
1943 mHasOtherFamilyNames
=
1944 ReadOtherFamilyNamesForFace(aPlatformFontList
, nameTable
);
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
) {
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
];
1962 gfxFontEntry::AutoTable
nameTable(fe
, kNAME
);
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)");
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)");
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
)) {
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
2018 mCheckedForLegacyFamilyNames
= true;
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
2027 AutoReadLock
lock(mLock
);
2028 faces
.AppendElements(mAvailableFonts
);
2030 for (const auto& fe
: faces
) {
2034 gfxFontEntry::AutoTable
nameTable(fe
, kNAME
);
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
)) {
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
)) {
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
)) {
2080 FindStyleVariationsLocked(aFontInfoData
);
2082 // check again, as style enumeration code may have loaded names
2083 if (mOtherFamilyNamesInitialized
&&
2084 (mFaceNamesInitialized
|| !aNeedFullnamePostscriptNames
)) {
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
];
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
) {
2117 // load directly from the name table
2118 gfxFontEntry::AutoTable
nameTable(fe
, kNAME
);
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;
2148 // if not reading in any more names, skip other faces
2149 if (!readAllFaces
&& !aNeedFullnamePostscriptNames
) {
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
)) {
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
) {
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
];
2202 fe
->AddSizeOfIncludingThis(aMallocSizeOf
, aSizes
);
2207 void gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
2208 FontListSizes
* aSizes
) const {
2209 aSizes
->mFontListSize
+= aMallocSizeOf(this);
2210 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);