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