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