SharedFontList.cpp (50056B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "SharedFontList-impl.h" 6 #include "gfxPlatformFontList.h" 7 #include "gfxFontUtils.h" 8 #include "gfxFont.h" 9 #include "nsReadableUtils.h" 10 #include "prerror.h" 11 #include "mozilla/dom/ContentChild.h" 12 #include "mozilla/dom/ContentParent.h" 13 #include "mozilla/Logging.h" 14 15 #define LOG_FONTLIST(args) \ 16 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args) 17 #define LOG_FONTLIST_ENABLED() \ 18 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug) 19 20 namespace mozilla { 21 namespace fontlist { 22 23 static double WSSDistance(const Face* aFace, const gfxFontStyle& aStyle) { 24 double stretchDist = StretchDistance(aFace->mStretch, aStyle.stretch); 25 double styleDist = StyleDistance( 26 aFace->mStyle, aStyle.style, 27 aStyle.synthesisStyle != StyleFontSynthesisStyle::ObliqueOnly); 28 double weightDist = WeightDistance(aFace->mWeight, aStyle.weight); 29 30 // Sanity-check that the distances are within the expected range 31 // (update if implementation of the distance functions is changed). 32 MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0); 33 MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 900.0); 34 MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0); 35 36 // weight/style/stretch priority: stretch >> style >> weight 37 // so we multiply the stretch and style values to make them dominate 38 // the result 39 return stretchDist * kStretchFactor + styleDist * kStyleFactor + 40 weightDist * kWeightFactor; 41 } 42 43 void* Pointer::ToPtr(FontList* aFontList, 44 size_t aSize) const MOZ_NO_THREAD_SAFETY_ANALYSIS { 45 // On failure, we'll return null; callers need to handle this appropriately 46 // (e.g. via fallback). 47 void* result = nullptr; 48 49 if (IsNull()) { 50 return result; 51 } 52 53 // Ensure the list doesn't get replaced out from under us. Font-list rebuild 54 // happens on the main thread, so only non-main-thread callers need to lock 55 // it here. 56 bool isMainThread = NS_IsMainThread(); 57 if (!isMainThread) { 58 gfxPlatformFontList::PlatformFontList()->Lock(); 59 } 60 61 uint32_t blockIndex = Block(); 62 63 // If the Pointer refers to a block we have not yet mapped in this process, 64 // we first need to retrieve new block handle(s) from the parent and update 65 // our mBlocks list. 66 auto& blocks = aFontList->mBlocks; 67 if (blockIndex >= blocks.Length()) { 68 if (MOZ_UNLIKELY(XRE_IsParentProcess())) { 69 // Shouldn't happen! A content process tried to pass a bad Pointer? 70 goto cleanup; 71 } 72 // If we're not on the main thread, we can't do the IPC involved in 73 // UpdateShmBlocks; just let the lookup fail for now. 74 if (!isMainThread) { 75 goto cleanup; 76 } 77 // UpdateShmBlocks can fail, if the parent has replaced the font list with 78 // a new generation. In that case we just return null, and whatever font 79 // the content process was trying to use will appear unusable for now. It's 80 // about to receive a notification of the new font list anyhow, at which 81 // point it will flush its caches and reflow everything, so the temporary 82 // failure of this font will be forgotten. 83 // UpdateShmBlocks will take the platform font-list lock during the update. 84 if (MOZ_UNLIKELY(!aFontList->UpdateShmBlocks(true))) { 85 goto cleanup; 86 } 87 MOZ_ASSERT(blockIndex < blocks.Length(), "failure in UpdateShmBlocks?"); 88 // This is wallpapering bug 1667977; it's unclear if we will always survive 89 // this, as the content process may be unable to shape/render text if all 90 // font lookups are failing. 91 // In at least some cases, however, this can occur transiently while the 92 // font list is being rebuilt by the parent; content will then be notified 93 // that the list has changed, and should refresh everything successfully. 94 if (MOZ_UNLIKELY(blockIndex >= blocks.Length())) { 95 goto cleanup; 96 } 97 } 98 99 { 100 // Don't create a pointer that's outside what the block has allocated! 101 const auto& block = blocks[blockIndex]; 102 if (MOZ_LIKELY(Offset() + aSize <= block->Allocated())) { 103 result = static_cast<char*>(block->Memory()) + Offset(); 104 } 105 } 106 107 cleanup: 108 if (!isMainThread) { 109 gfxPlatformFontList::PlatformFontList()->Unlock(); 110 } 111 112 return result; 113 } 114 115 void String::Assign(const nsACString& aString, FontList* aList) { 116 // We only assign to previously-empty strings; they are never modified 117 // after initial assignment. 118 MOZ_ASSERT(mPointer.IsNull()); 119 mLength = aString.Length(); 120 mPointer = aList->Alloc(mLength + 1); 121 auto* p = mPointer.ToArray<char>(aList, mLength); 122 std::memcpy(p, aString.BeginReading(), mLength); 123 p[mLength] = '\0'; 124 } 125 126 Family::Family(FontList* aList, const InitData& aData) 127 : mFaceCount(0), 128 mKey(aList, aData.mKey), 129 mName(aList, aData.mName), 130 mCharacterMap(Pointer::Null()), 131 mFaces(Pointer::Null()), 132 mIndex(aData.mIndex), 133 mVisibility(aData.mVisibility), 134 mIsSimple(false), 135 mIsBundled(aData.mBundled), 136 mIsBadUnderlineFamily(aData.mBadUnderline), 137 mIsForceClassic(aData.mForceClassic), 138 mIsAltLocale(aData.mAltLocale) {} 139 140 class SetCharMapRunnable : public mozilla::Runnable { 141 public: 142 SetCharMapRunnable(uint32_t aListGeneration, 143 std::pair<uint32_t, bool> aFamilyIndex, 144 uint32_t aFaceIndex, gfxCharacterMap* aCharMap) 145 : Runnable("SetCharMapRunnable"), 146 mListGeneration(aListGeneration), 147 mFamilyIndex(aFamilyIndex), 148 mFaceIndex(aFaceIndex), 149 mCharMap(aCharMap) {} 150 151 NS_IMETHOD Run() override { 152 auto* list = gfxPlatformFontList::PlatformFontList()->SharedFontList(); 153 if (!list || list->GetGeneration() != mListGeneration) { 154 return NS_OK; 155 } 156 dom::ContentChild::GetSingleton()->SendSetCharacterMap( 157 mListGeneration, mFamilyIndex.first, mFamilyIndex.second, mFaceIndex, 158 *mCharMap); 159 return NS_OK; 160 } 161 162 private: 163 uint32_t mListGeneration; 164 std::pair<uint32_t, bool> mFamilyIndex; 165 uint32_t mFaceIndex; 166 RefPtr<gfxCharacterMap> mCharMap; 167 }; 168 169 void Face::SetCharacterMap(FontList* aList, gfxCharacterMap* aCharMap, 170 const Family* aFamily) { 171 if (!XRE_IsParentProcess()) { 172 Maybe<std::pair<uint32_t, bool>> familyIndex = aFamily->FindIndex(aList); 173 if (!familyIndex) { 174 NS_WARNING("Family index not found! Ignoring SetCharacterMap"); 175 return; 176 } 177 const auto* faces = aFamily->Faces(aList); 178 uint32_t faceIndex = 0; 179 while (faceIndex < aFamily->NumFaces()) { 180 if (faces[faceIndex].ToPtr<Face>(aList) == this) { 181 break; 182 } 183 ++faceIndex; 184 } 185 if (faceIndex >= aFamily->NumFaces()) { 186 NS_WARNING("Face not found in family! Ignoring SetCharacterMap"); 187 return; 188 } 189 if (NS_IsMainThread()) { 190 dom::ContentChild::GetSingleton()->SendSetCharacterMap( 191 aList->GetGeneration(), familyIndex->first, familyIndex->second, 192 faceIndex, *aCharMap); 193 } else { 194 NS_DispatchToMainThread(new SetCharMapRunnable( 195 aList->GetGeneration(), familyIndex.value(), faceIndex, aCharMap)); 196 } 197 return; 198 } 199 auto pfl = gfxPlatformFontList::PlatformFontList(); 200 mCharacterMap = pfl->GetShmemCharMap(aCharMap); 201 } 202 203 void Family::AddFaces(FontList* aList, const nsTArray<Face::InitData>& aFaces) { 204 MOZ_ASSERT(XRE_IsParentProcess()); 205 if (mFaceCount > 0) { 206 // Already initialized! 207 return; 208 } 209 210 uint32_t count = aFaces.Length(); 211 bool isSimple = false; 212 // A family is "simple" (i.e. simplified style selection may be used instead 213 // of the full CSS font-matching algorithm) if there is at maximum one normal, 214 // bold, italic, and bold-italic face; in this case, they are stored at known 215 // positions in the mFaces array. 216 const Face::InitData* slots[4] = {nullptr, nullptr, nullptr, nullptr}; 217 if (count >= 2 && count <= 4) { 218 // Check if this can be treated as a "simple" family 219 isSimple = true; 220 for (const auto& f : aFaces) { 221 if (!f.mWeight.IsSingle() || !f.mStretch.IsSingle() || 222 !f.mStyle.IsSingle()) { 223 isSimple = false; 224 break; 225 } 226 if (!f.mStretch.Min().IsNormal()) { 227 isSimple = false; 228 break; 229 } 230 // Figure out which slot (0-3) this face belongs in 231 size_t slot = 0; 232 static_assert((kBoldMask | kItalicMask) == 0b11, "bad bold/italic bits"); 233 if (f.mWeight.Min().IsBold()) { 234 slot |= kBoldMask; 235 } 236 if (!f.mStyle.Min().IsNormal()) { 237 slot |= kItalicMask; 238 } 239 if (slots[slot]) { 240 // More than one face mapped to the same slot - not a simple family! 241 isSimple = false; 242 break; 243 } 244 slots[slot] = &f; 245 } 246 if (isSimple) { 247 // Ensure all 4 slots will exist, even if some are empty. 248 count = 4; 249 } 250 } 251 252 // Allocate space for the face records, and initialize them. 253 // coverity[suspicious_sizeof] 254 Pointer p = aList->Alloc(count * sizeof(Pointer)); 255 auto* facePtrs = p.ToArray<Pointer>(aList, count); 256 for (size_t i = 0; i < count; i++) { 257 if (isSimple && !slots[i]) { 258 facePtrs[i] = Pointer::Null(); 259 } else { 260 const auto* initData = isSimple ? slots[i] : &aFaces[i]; 261 Pointer fp = aList->Alloc(sizeof(Face)); 262 auto* face = fp.ToPtr<Face>(aList); 263 (void)new (face) Face(aList, *initData); 264 facePtrs[i] = fp; 265 if (initData->mCharMap) { 266 face->SetCharacterMap(aList, initData->mCharMap, this); 267 } 268 } 269 } 270 271 mIsSimple = isSimple; 272 mFaces = p; 273 mFaceCount.store(count); 274 275 if (LOG_FONTLIST_ENABLED()) { 276 const nsCString& fam = DisplayName().AsString(aList); 277 for (unsigned j = 0; j < aFaces.Length(); j++) { 278 nsAutoCString weight, style, stretch; 279 aFaces[j].mWeight.ToString(weight); 280 aFaces[j].mStyle.ToString(style); 281 aFaces[j].mStretch.ToString(stretch); 282 LOG_FONTLIST( 283 ("(shared-fontlist) family (%s) added face (%s) index %u, weight " 284 "%s, style %s, stretch %s", 285 fam.get(), aFaces[j].mDescriptor.get(), aFaces[j].mIndex, 286 weight.get(), style.get(), stretch.get())); 287 } 288 } 289 } 290 291 bool Family::FindAllFacesForStyleInternal(FontList* aList, 292 const gfxFontStyle& aStyle, 293 nsTArray<Face*>& aFaceList) const { 294 MOZ_ASSERT(aFaceList.IsEmpty()); 295 if (!IsInitialized()) { 296 return false; 297 } 298 299 Pointer* facePtrs = Faces(aList); 300 if (!facePtrs) { 301 return false; 302 } 303 304 // Depending on the kind of family, we have to do varying amounts of work 305 // to figure out what face(s) to use for the requested style properties. 306 307 // If the family has only one face, we simply use it; no further style 308 // checking needed. (However, for bitmap fonts we may still need to check 309 // whether the size is acceptable.) 310 if (NumFaces() == 1) { 311 MOZ_ASSERT(!facePtrs[0].IsNull()); 312 auto* face = facePtrs[0].ToPtr<Face>(aList); 313 if (face && face->HasValidDescriptor()) { 314 aFaceList.AppendElement(face); 315 #ifdef MOZ_WIDGET_GTK 316 if (face->mSize) { 317 return true; 318 } 319 #endif 320 } 321 return false; 322 } 323 324 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic, 325 // or some subset of these. In this case, we have exactly 4 entries in 326 // mAvailableFonts, stored in the above order; note that some of the entries 327 // may be nullptr. We can then pick the required entry based on whether the 328 // request is for bold or non-bold, italic or non-italic, without running 329 // the more complex matching algorithm used for larger families with many 330 // weights and/or widths. 331 332 if (mIsSimple) { 333 // Family has no more than the "standard" 4 faces, at fixed indexes; 334 // calculate which one we want. 335 // Note that we cannot simply return it as not all 4 faces are necessarily 336 // present. 337 bool wantBold = aStyle.weight.PreferBold(); 338 bool wantItalic = !aStyle.style.IsNormal(); 339 uint8_t faceIndex = 340 (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0); 341 342 // If the desired style is available, use it directly. 343 auto* face = facePtrs[faceIndex].ToPtr<Face>(aList); 344 if (face && face->HasValidDescriptor()) { 345 aFaceList.AppendElement(face); 346 #ifdef MOZ_WIDGET_GTK 347 if (face->mSize) { 348 return true; 349 } 350 #endif 351 return false; 352 } 353 354 // Order to check fallback faces in a simple family, depending on the 355 // requested style. 356 static const uint8_t simpleFallbacks[4][3] = { 357 {kBoldFaceIndex, kItalicFaceIndex, 358 kBoldItalicFaceIndex}, // fallback sequence for Regular 359 {kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex}, // Bold 360 {kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex}, // Italic 361 {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex} // BoldItalic 362 }; 363 const uint8_t* order = simpleFallbacks[faceIndex]; 364 365 for (uint8_t trial = 0; trial < 3; ++trial) { 366 // check remaining faces in order of preference to find the first that 367 // actually exists 368 face = facePtrs[order[trial]].ToPtr<Face>(aList); 369 if (face && face->HasValidDescriptor()) { 370 aFaceList.AppendElement(face); 371 #ifdef MOZ_WIDGET_GTK 372 if (face->mSize) { 373 return true; 374 } 375 #endif 376 return false; 377 } 378 } 379 380 // We can only reach here if we failed to resolve the face pointer, which 381 // can happen if we're on a stylo thread and caught the font list being 382 // updated; in that case we just fail quietly and let font fallback do 383 // something for the time being. 384 return false; 385 } 386 387 // Pick the font(s) that are closest to the desired weight, style, and 388 // stretch. Iterate over all fonts, measuring the weight/style distance. 389 // Because of unicode-range values, there may be more than one font for a 390 // given but the 99% use case is only a single font entry per 391 // weight/style/stretch distance value. To optimize this, only add entries 392 // to the matched font array when another entry already has the same 393 // weight/style/stretch distance and add the last matched font entry. For 394 // normal platform fonts with a single font entry for each 395 // weight/style/stretch combination, only the last matched font entry will 396 // be added. 397 double minDistance = INFINITY; 398 Face* matched = nullptr; 399 // Keep track of whether we've included any non-scalable font resources in 400 // the selected set. 401 bool anyNonScalable = false; 402 for (uint32_t i = 0; i < NumFaces(); i++) { 403 auto* face = facePtrs[i].ToPtr<Face>(aList); 404 if (face) { 405 // weight/style/stretch priority: stretch >> style >> weight 406 double distance = WSSDistance(face, aStyle); 407 if (distance < minDistance) { 408 matched = face; 409 if (!aFaceList.IsEmpty()) { 410 aFaceList.Clear(); 411 } 412 minDistance = distance; 413 } else if (distance == minDistance) { 414 if (matched) { 415 aFaceList.AppendElement(matched); 416 #ifdef MOZ_WIDGET_GTK 417 if (matched->mSize) { 418 anyNonScalable = true; 419 } 420 #endif 421 } 422 matched = face; 423 } 424 } 425 } 426 427 MOZ_ASSERT(matched, "didn't match a font within a family"); 428 if (matched) { 429 aFaceList.AppendElement(matched); 430 #ifdef MOZ_WIDGET_GTK 431 if (matched->mSize) { 432 anyNonScalable = true; 433 } 434 #endif 435 } 436 437 return anyNonScalable; 438 } 439 440 void Family::FindAllFacesForStyle(FontList* aList, const gfxFontStyle& aStyle, 441 nsTArray<Face*>& aFaceList, 442 bool aIgnoreSizeTolerance) const { 443 #ifdef MOZ_WIDGET_GTK 444 bool anyNonScalable = 445 #else 446 (void) 447 #endif 448 FindAllFacesForStyleInternal(aList, aStyle, aFaceList); 449 450 #ifdef MOZ_WIDGET_GTK 451 // aFaceList now contains whatever faces are the best style match for 452 // the requested style. If specifically-sized bitmap faces are supported, 453 // we need to additionally filter the list to choose the appropriate size. 454 // 455 // It would be slightly more efficient to integrate this directly into the 456 // face-selection algorithm above, but it's a rare case that doesn't apply 457 // at all to most font families. 458 // 459 // Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e. 460 // when using the gfxFcPlatformFontList implementation), so this filtering is 461 // not needed on other platforms. 462 // 463 // (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color 464 // Emoji don't count here; they package multiple bitmap sizes into a single 465 // OpenType wrapper, so they appear as a single "scalable" face in our list.) 466 if (anyNonScalable) { 467 uint16_t best = 0; 468 gfxFloat dist = 0.0; 469 for (const auto& f : aFaceList) { 470 if (f->mSize == 0) { 471 // Scalable face; no size distance to compute. 472 continue; 473 } 474 gfxFloat d = fabs(gfxFloat(f->mSize) - aStyle.size); 475 if (!aIgnoreSizeTolerance && (d * 5.0 > f->mSize)) { 476 continue; // Too far from the requested size, ignore. 477 } 478 // If we haven't found a "best" bitmap size yet, or if this is a better 479 // match, remember it. 480 if (!best || d < dist) { 481 best = f->mSize; 482 dist = d; 483 } 484 } 485 // Discard all faces except the chosen "best" size; or if no pixel size was 486 // chosen, all except scalable faces. 487 // This may eliminate *all* faces in the family, if all were bitmaps and 488 // none was a good enough size match, in which case we'll fall back to the 489 // next font-family name. 490 aFaceList.RemoveElementsBy([=](const auto& e) { return e->mSize != best; }); 491 } 492 #endif 493 } 494 495 Face* Family::FindFaceForStyle(FontList* aList, const gfxFontStyle& aStyle, 496 bool aIgnoreSizeTolerance) const { 497 AutoTArray<Face*, 4> faces; 498 FindAllFacesForStyle(aList, aStyle, faces, aIgnoreSizeTolerance); 499 return faces.IsEmpty() ? nullptr : faces[0]; 500 } 501 502 void Family::SearchAllFontsForChar(FontList* aList, 503 GlobalFontMatch* aMatchData) { 504 auto* charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList); 505 if (!charmap) { 506 // If the face list is not yet initialized, or if character maps have 507 // not been loaded, go ahead and do this now (by sending a message to the 508 // parent process, if we're running in a child). 509 // After this, all faces should have their mCharacterMap set up, and the 510 // family's mCharacterMap should also be set; but in the code below we 511 // don't assume this all succeeded, so it still checks. 512 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this, 513 true)) { 514 return; 515 } 516 charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList); 517 } 518 if (charmap && !charmap->test(aMatchData->mCh)) { 519 return; 520 } 521 522 uint32_t numFaces = NumFaces(); 523 uint32_t charMapsLoaded = 0; // number of faces whose charmap is loaded 524 Pointer* facePtrs = Faces(aList); 525 if (!facePtrs) { 526 return; 527 } 528 for (uint32_t i = 0; i < numFaces; i++) { 529 auto* face = facePtrs[i].ToPtr<Face>(aList); 530 if (!face) { 531 continue; 532 } 533 MOZ_ASSERT(face->HasValidDescriptor()); 534 // Get the face's character map, if available (may be null!) 535 charmap = face->mCharacterMap.ToPtr<const SharedBitSet>(aList); 536 if (charmap) { 537 ++charMapsLoaded; 538 } 539 // Check style distance if the char is supported, or if charmap not known 540 // (so that we don't trigger cmap-loading for faces that would be a worse 541 // match than what we've already found). 542 if (!charmap || charmap->test(aMatchData->mCh)) { 543 double distance = WSSDistance(face, aMatchData->mStyle); 544 if (distance < aMatchData->mMatchDistance) { 545 // It's a better style match: get a fontEntry, and if we haven't 546 // already checked character coverage, do it now (note that 547 // HasCharacter() will trigger loading the fontEntry's cmap, if 548 // needed). 549 RefPtr<gfxFontEntry> fe = 550 gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face, 551 this); 552 if (!fe) { 553 continue; 554 } 555 if (!charmap && !fe->HasCharacter(aMatchData->mCh)) { 556 continue; 557 } 558 if (aMatchData->mPresentation != FontPresentation::Any) { 559 RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle); 560 if (!font) { 561 continue; 562 } 563 bool hasColorGlyph = 564 font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh); 565 if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) { 566 distance += kPresentationMismatch; 567 if (distance >= aMatchData->mMatchDistance) { 568 continue; 569 } 570 } 571 } 572 aMatchData->mBestMatch = fe; 573 aMatchData->mMatchDistance = distance; 574 aMatchData->mMatchedSharedFamily = this; 575 } 576 } 577 } 578 if (mCharacterMap.IsNull() && charMapsLoaded == numFaces) { 579 SetupFamilyCharMap(aList); 580 } 581 } 582 583 void Family::SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces) { 584 if (aFaces.Length() >= 2 && aFaces.Length() <= 4) { 585 // Check whether the faces meet the criteria for a "simple" family: no more 586 // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store 587 // them at the appropriate slots in mFaces and set the mIsSimple flag to 588 // accelerate font-matching. 589 bool isSimple = true; 590 Pointer slots[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(), 591 Pointer::Null()}; 592 for (const Pointer& fp : aFaces) { 593 auto* f = fp.ToPtr<const Face>(aList); 594 if (!f->mWeight.IsSingle() || !f->mStyle.IsSingle() || 595 !f->mStretch.IsSingle()) { 596 isSimple = false; 597 break; 598 } 599 if (!f->mStretch.Min().IsNormal()) { 600 isSimple = false; 601 break; 602 } 603 size_t slot = 0; 604 if (f->mWeight.Min().IsBold()) { 605 slot |= kBoldMask; 606 } 607 if (!f->mStyle.Min().IsNormal()) { 608 slot |= kItalicMask; 609 } 610 if (!slots[slot].IsNull()) { 611 isSimple = false; 612 break; 613 } 614 slots[slot] = fp; 615 } 616 if (isSimple) { 617 size_t size = 4 * sizeof(Pointer); 618 mFaces = aList->Alloc(size); 619 memcpy(mFaces.ToPtr(aList, size), slots, size); 620 mFaceCount.store(4); 621 mIsSimple = true; 622 return; 623 } 624 } 625 size_t size = aFaces.Length() * sizeof(Pointer); 626 mFaces = aList->Alloc(size); 627 memcpy(mFaces.ToPtr(aList, size), aFaces.Elements(), size); 628 mFaceCount.store(aFaces.Length()); 629 } 630 631 void Family::SetupFamilyCharMap(FontList* aList) { 632 // Set the character map of the family to the union of all the face cmaps, 633 // to allow font fallback searches to more rapidly reject the family. 634 if (!mCharacterMap.IsNull()) { 635 return; 636 } 637 if (!XRE_IsParentProcess()) { 638 // |this| could be a Family record in either the Families() or Aliases() 639 // arrays; FindIndex will map it back to its index and which array. 640 Maybe<std::pair<uint32_t, bool>> index = FindIndex(aList); 641 if (!index) { 642 NS_WARNING("Family index not found! Ignoring SetupFamilyCharMap"); 643 return; 644 } 645 if (NS_IsMainThread()) { 646 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap( 647 aList->GetGeneration(), index->first, index->second); 648 return; 649 } 650 NS_DispatchToMainThread(NS_NewRunnableFunction( 651 "SetupFamilyCharMap callback", 652 [gen = aList->GetGeneration(), idx = index->first, 653 alias = index->second] { 654 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(gen, idx, 655 alias); 656 })); 657 return; 658 } 659 gfxSparseBitSet familyMap; 660 Pointer firstMapShmPointer; 661 const SharedBitSet* firstMap = nullptr; 662 bool merged = false; 663 Pointer* faces = Faces(aList); 664 if (!faces) { 665 return; 666 } 667 for (size_t i = 0; i < NumFaces(); i++) { 668 auto* f = faces[i].ToPtr<const Face>(aList); 669 if (!f) { 670 continue; // Skip missing face (in an incomplete "simple" family) 671 } 672 auto* faceMap = f->mCharacterMap.ToPtr<const SharedBitSet>(aList); 673 if (!faceMap) { 674 continue; // If there's a face where setting up the cmap failed, we skip 675 // it as unusable. 676 } 677 if (!firstMap) { 678 firstMap = faceMap; 679 firstMapShmPointer = f->mCharacterMap; 680 } else if (faceMap != firstMap) { 681 if (!merged) { 682 familyMap.Union(*firstMap); 683 merged = true; 684 } 685 familyMap.Union(*faceMap); 686 } 687 } 688 // If we created a merged cmap, we need to save that on the family; or if we 689 // found no usable cmaps at all, we need to store the empty familyMap so that 690 // we won't repeatedly attempt this for an unusable family. 691 if (merged || firstMapShmPointer.IsNull()) { 692 mCharacterMap = 693 gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap); 694 } else { 695 // If all [usable] faces had the same cmap, we can just share it. 696 mCharacterMap = firstMapShmPointer; 697 } 698 } 699 700 Maybe<std::pair<uint32_t, bool>> Family::FindIndex(FontList* aList) const { 701 const auto* start = aList->Families(); 702 const auto* end = start + aList->NumFamilies(); 703 if (this >= start && this < end) { 704 uint32_t index = this - start; 705 MOZ_RELEASE_ASSERT(start + index == this, "misaligned Family ptr!"); 706 return Some(std::pair(index, false)); 707 } 708 709 start = aList->AliasFamilies(); 710 end = start + aList->NumAliases(); 711 if (this >= start && this < end) { 712 uint32_t index = this - start; 713 MOZ_RELEASE_ASSERT(start + index == this, "misaligned AliasFamily ptr!"); 714 return Some(std::pair(index, true)); 715 } 716 717 return Nothing(); 718 } 719 720 FontList::FontList(uint32_t aGeneration) { 721 if (XRE_IsParentProcess()) { 722 // Create the initial shared block, and initialize Header 723 if (AppendShmBlock(SHM_BLOCK_SIZE)) { 724 Header& header = GetHeader(); 725 header.mBlockHeader.mAllocated.store(sizeof(Header)); 726 header.mGeneration = aGeneration; 727 header.mFamilyCount = 0; 728 header.mBlockCount.store(1); 729 header.mAliasCount.store(0); 730 header.mLocalFaceCount.store(0); 731 header.mFamilies = Pointer::Null(); 732 header.mAliases = Pointer::Null(); 733 header.mLocalFaces = Pointer::Null(); 734 } else { 735 MOZ_CRASH("parent: failed to initialize FontList"); 736 } 737 } else { 738 // Initialize using the list of shmem blocks passed by the parent via 739 // SetXPCOMProcessAttributes. 740 auto& blocks = dom::ContentChild::GetSingleton()->SharedFontListBlocks(); 741 for (auto& handle : blocks) { 742 if (!handle) { 743 // Bail out and let UpdateShmBlocks try to do its thing below. 744 break; 745 } 746 if (handle.Size() < SHM_BLOCK_SIZE) { 747 MOZ_CRASH("failed to map shared memory"); 748 } 749 auto newShm = handle.Map(); 750 if (!newShm || !newShm.Address()) { 751 MOZ_CRASH("failed to map shared memory"); 752 } 753 uint32_t size = newShm.DataAs<BlockHeader>()->mBlockSize; 754 MOZ_ASSERT(size >= SHM_BLOCK_SIZE); 755 if (newShm.Size() < size) { 756 MOZ_CRASH("failed to map shared memory"); 757 } 758 mBlocks.AppendElement(new ShmBlock(std::move(newShm))); 759 } 760 blocks.Clear(); 761 // Update in case of any changes since the initial message was sent. 762 for (unsigned retryCount = 0; retryCount < 3; ++retryCount) { 763 if (UpdateShmBlocks(false)) { 764 return; 765 } 766 // The only reason for UpdateShmBlocks to fail is if the parent recreated 767 // the list after we read its first block, but before we finished getting 768 // them all, and so the generation check failed on a subsequent request. 769 // Discarding whatever we've got and retrying should get us a new, 770 // consistent set of memory blocks in this case. If this doesn't work 771 // after a couple of retries, bail out. 772 DetachShmBlocks(); 773 } 774 NS_WARNING("child: failed to initialize shared FontList"); 775 } 776 } 777 778 FontList::~FontList() { DetachShmBlocks(); } 779 780 FontList::Header& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS { 781 // We only need to lock if we're not on the main thread. 782 bool isMainThread = NS_IsMainThread(); 783 if (!isMainThread) { 784 gfxPlatformFontList::PlatformFontList()->Lock(); 785 } 786 787 // It's invalid to try and access this before the first block exists. 788 MOZ_ASSERT(mBlocks.Length() > 0); 789 auto& result = *static_cast<Header*>(mBlocks[0]->Memory()); 790 791 if (!isMainThread) { 792 gfxPlatformFontList::PlatformFontList()->Unlock(); 793 } 794 795 return result; 796 } 797 798 bool FontList::AppendShmBlock(uint32_t aSizeNeeded) { 799 MOZ_ASSERT(XRE_IsParentProcess()); 800 uint32_t size = std::max(aSizeNeeded, SHM_BLOCK_SIZE); 801 auto handle = ipc::shared_memory::CreateFreezable(size); 802 if (!handle) { 803 MOZ_CRASH("failed to create shared memory"); 804 return false; 805 } 806 auto [readOnly, newShm] = std::move(handle).Map().FreezeWithMutableMapping(); 807 if (!newShm || !newShm.Address()) { 808 MOZ_CRASH("failed to map shared memory"); 809 return false; 810 } 811 if (!readOnly) { 812 MOZ_CRASH("failed to create read-only copy"); 813 return false; 814 } 815 816 ShmBlock* block = new ShmBlock(std::move(newShm)); 817 block->StoreAllocated(sizeof(BlockHeader)); 818 block->BlockSize() = size; 819 820 mBlocks.AppendElement(block); 821 GetHeader().mBlockCount.store(mBlocks.Length()); 822 823 mReadOnlyShmems.AppendElement(std::move(readOnly)); 824 825 // We don't need to broadcast the addition of the initial block, 826 // because child processes can't have initialized their list at all 827 // prior to the first block being set up. 828 if (mBlocks.Length() > 1) { 829 if (NS_IsMainThread()) { 830 dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(), 831 mBlocks.Length() - 1); 832 } else { 833 NS_DispatchToMainThread(NS_NewRunnableFunction( 834 "ShmBlockAdded callback", 835 [generation = GetGeneration(), index = mBlocks.Length() - 1] { 836 dom::ContentParent::BroadcastShmBlockAdded(generation, index); 837 })); 838 } 839 } 840 841 return true; 842 } 843 844 void FontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex, 845 ipc::ReadOnlySharedMemoryHandle aHandle) { 846 MOZ_ASSERT(!XRE_IsParentProcess()); 847 MOZ_ASSERT(mBlocks.Length() > 0); 848 849 if (!aHandle) { 850 return; 851 } 852 if (aIndex != mBlocks.Length()) { 853 return; 854 } 855 if (aGeneration != GetGeneration()) { 856 return; 857 } 858 859 auto newShm = aHandle.Map(); 860 if (!newShm || !newShm.Address() || newShm.Size() < SHM_BLOCK_SIZE) { 861 MOZ_CRASH("failed to map shared memory"); 862 } 863 864 uint32_t size = newShm.DataAs<BlockHeader>()->mBlockSize; 865 MOZ_ASSERT(size >= SHM_BLOCK_SIZE); 866 if (newShm.Size() < size) { 867 MOZ_CRASH("failed to map shared memory"); 868 } 869 870 mBlocks.AppendElement(new ShmBlock(std::move(newShm))); 871 } 872 873 void FontList::DetachShmBlocks() { 874 for (auto& i : mBlocks) { 875 i->Clear(); 876 } 877 mBlocks.Clear(); 878 mReadOnlyShmems.Clear(); 879 } 880 881 FontList::ShmBlock* FontList::GetBlockFromParent(uint32_t aIndex) { 882 MOZ_ASSERT(!XRE_IsParentProcess()); 883 // If we have no existing blocks, we don't want a generation check yet; 884 // the header in the first block will define the generation of this list 885 uint32_t generation = aIndex == 0 ? 0 : GetGeneration(); 886 ipc::ReadOnlySharedMemoryHandle handle; 887 if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock( 888 generation, aIndex, &handle)) { 889 return nullptr; 890 } 891 if (!handle) { 892 return nullptr; 893 } 894 auto newShm = handle.Map(); 895 if (!newShm || !newShm.Address() || newShm.Size() < SHM_BLOCK_SIZE) { 896 MOZ_CRASH("failed to map shared memory"); 897 } 898 uint32_t size = newShm.DataAs<BlockHeader>()->mBlockSize; 899 MOZ_ASSERT(size >= SHM_BLOCK_SIZE); 900 if (newShm.Size() < size) { 901 MOZ_CRASH("failed to map shared memory"); 902 } 903 return new ShmBlock(std::move(newShm)); 904 } 905 906 // We don't take the lock when called from the constructor, so disable thread- 907 // safety analysis here. 908 bool FontList::UpdateShmBlocks(bool aMustLock) MOZ_NO_THREAD_SAFETY_ANALYSIS { 909 MOZ_ASSERT(!XRE_IsParentProcess()); 910 if (aMustLock) { 911 gfxPlatformFontList::PlatformFontList()->Lock(); 912 } 913 bool result = true; 914 while (!mBlocks.Length() || mBlocks.Length() < GetHeader().mBlockCount) { 915 ShmBlock* newBlock = GetBlockFromParent(mBlocks.Length()); 916 if (!newBlock) { 917 result = false; 918 break; 919 } 920 mBlocks.AppendElement(newBlock); 921 } 922 if (aMustLock) { 923 gfxPlatformFontList::PlatformFontList()->Unlock(); 924 } 925 return result; 926 } 927 928 void FontList::ShareBlocksToProcess( 929 nsTArray<ipc::ReadOnlySharedMemoryHandle>* aBlocks, base::ProcessId aPid) { 930 MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); 931 for (auto& shmem : mReadOnlyShmems) { 932 auto handle = shmem.Clone(); 933 if (!handle) { 934 // If something went wrong here, we just bail out; the child will need to 935 // request the blocks as needed, at some performance cost. (Although in 936 // practice this may mean resources are so constrained the child process 937 // isn't really going to work at all. But that's not our problem here.) 938 aBlocks->Clear(); 939 return; 940 } 941 aBlocks->AppendElement(std::move(handle)); 942 } 943 } 944 945 ipc::ReadOnlySharedMemoryHandle FontList::ShareBlockToProcess( 946 uint32_t aIndex, base::ProcessId aPid) { 947 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 948 MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length()); 949 MOZ_RELEASE_ASSERT(aIndex < mReadOnlyShmems.Length()); 950 951 return mReadOnlyShmems[aIndex].Clone(); 952 } 953 954 Pointer FontList::Alloc(uint32_t aSize) { 955 // Only the parent process does allocation. 956 MOZ_ASSERT(XRE_IsParentProcess()); 957 958 // 4-byte alignment is good enough for anything we allocate in the font list, 959 // as our "Pointer" (block index/offset) is a 32-bit value even on x64. 960 auto align = [](uint32_t aSize) -> size_t { return (aSize + 3u) & ~3u; }; 961 962 aSize = align(aSize); 963 964 int32_t blockIndex = -1; 965 uint32_t curAlloc, size; 966 967 if (aSize < SHM_BLOCK_SIZE - sizeof(BlockHeader)) { 968 // Try to allocate in the most recently added block first, as this is 969 // highly likely to succeed; if not, try earlier blocks (to fill gaps). 970 const int32_t blockCount = mBlocks.Length(); 971 for (blockIndex = blockCount - 1; blockIndex >= 0; --blockIndex) { 972 size = mBlocks[blockIndex]->BlockSize(); 973 curAlloc = mBlocks[blockIndex]->Allocated(); 974 if (size - curAlloc >= aSize) { 975 break; 976 } 977 } 978 } 979 980 if (blockIndex < 0) { 981 // Couldn't find enough space (or the requested size is too large to use 982 // a part of a block): create a new block. 983 if (!AppendShmBlock(aSize + sizeof(BlockHeader))) { 984 return Pointer::Null(); 985 } 986 blockIndex = mBlocks.Length() - 1; 987 curAlloc = mBlocks[blockIndex]->Allocated(); 988 } 989 990 // We've found a block; allocate space from it, and return 991 mBlocks[blockIndex]->StoreAllocated(curAlloc + aSize); 992 993 return Pointer(blockIndex, curAlloc); 994 } 995 996 void FontList::SetFamilyNames(nsTArray<Family::InitData>& aFamilies) { 997 // Only the parent process should ever assign the list of families. 998 MOZ_ASSERT(XRE_IsParentProcess()); 999 1000 Header& header = GetHeader(); 1001 MOZ_ASSERT(!header.mFamilyCount); 1002 1003 gfxPlatformFontList::PlatformFontList()->ApplyWhitelist(aFamilies); 1004 aFamilies.Sort(); 1005 1006 size_t count = aFamilies.Length(); 1007 1008 // Any font resources with an empty family-name will have been collected in 1009 // a family with empty name, and sorted to the start of the list. Such fonts 1010 // are not generally usable, or recognized as "installed", so we drop them. 1011 if (count > 1 && aFamilies[0].mKey.IsEmpty()) { 1012 aFamilies.RemoveElementAt(0); 1013 --count; 1014 } 1015 1016 // Check for duplicate family entries (can occur if there is a bundled font 1017 // that has the same name as a system-installed one); in this case we keep 1018 // the bundled one as it will always be exposed. 1019 if (count > 1) { 1020 for (size_t i = 1; i < count; ++i) { 1021 if (aFamilies[i].mKey.Equals(aFamilies[i - 1].mKey)) { 1022 // Decide whether to discard the current entry or the preceding one 1023 size_t discard = 1024 aFamilies[i].mBundled && !aFamilies[i - 1].mBundled ? i - 1 : i; 1025 aFamilies.RemoveElementAt(discard); 1026 --count; 1027 --i; 1028 } 1029 } 1030 } 1031 1032 header.mFamilies = Alloc(count * sizeof(Family)); 1033 if (header.mFamilies.IsNull()) { 1034 return; 1035 } 1036 1037 // We can't call Families() here because the mFamilyCount field has not yet 1038 // been set! 1039 auto* families = header.mFamilies.ToArray<Family>(this, count); 1040 for (size_t i = 0; i < count; i++) { 1041 (void)new (&families[i]) Family(this, aFamilies[i]); 1042 LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i, 1043 aFamilies[i].mName.get())); 1044 } 1045 1046 header.mFamilyCount = count; 1047 } 1048 1049 void FontList::SetAliases( 1050 nsClassHashtable<nsCStringHashKey, AliasData>& aAliasTable) { 1051 MOZ_ASSERT(XRE_IsParentProcess()); 1052 1053 Header& header = GetHeader(); 1054 1055 // Build an array of Family::InitData records based on the entries in 1056 // aAliasTable, then sort them and store into the fontlist. 1057 nsTArray<Family::InitData> aliasArray; 1058 aliasArray.SetCapacity(aAliasTable.Count()); 1059 for (const auto& entry : aAliasTable) { 1060 aliasArray.AppendElement(Family::InitData( 1061 entry.GetKey(), entry.GetData()->mBaseFamily, entry.GetData()->mIndex, 1062 entry.GetData()->mVisibility, entry.GetData()->mBundled, 1063 entry.GetData()->mBadUnderline, entry.GetData()->mForceClassic, true)); 1064 } 1065 aliasArray.Sort(); 1066 1067 size_t count = aliasArray.Length(); 1068 1069 // Drop any entry with empty family-name as being unusable. 1070 if (count && aliasArray[0].mKey.IsEmpty()) { 1071 aliasArray.RemoveElementAt(0); 1072 --count; 1073 } 1074 1075 if (count < header.mAliasCount) { 1076 // This shouldn't happen, but handle it safely by just bailing out. 1077 NS_WARNING("cannot reduce number of aliases"); 1078 return; 1079 } 1080 fontlist::Pointer ptr = Alloc(count * sizeof(Family)); 1081 auto* aliases = ptr.ToArray<Family>(this, count); 1082 for (size_t i = 0; i < count; i++) { 1083 (void)new (&aliases[i]) Family(this, aliasArray[i]); 1084 LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i, 1085 aliasArray[i].mKey.get(), aliasArray[i].mName.get())); 1086 aliases[i].SetFacePtrs(this, aAliasTable.Get(aliasArray[i].mKey)->mFaces); 1087 if (LOG_FONTLIST_ENABLED()) { 1088 const auto& faces = aAliasTable.Get(aliasArray[i].mKey)->mFaces; 1089 for (unsigned j = 0; j < faces.Length(); j++) { 1090 auto* face = faces[j].ToPtr<const Face>(this); 1091 const nsCString& desc = face->mDescriptor.AsString(this); 1092 nsAutoCString weight, style, stretch; 1093 face->mWeight.ToString(weight); 1094 face->mStyle.ToString(style); 1095 face->mStretch.ToString(stretch); 1096 LOG_FONTLIST( 1097 ("(shared-fontlist) face (%s) index %u, weight %s, style %s, " 1098 "stretch %s", 1099 desc.get(), face->mIndex, weight.get(), style.get(), 1100 stretch.get())); 1101 } 1102 } 1103 } 1104 1105 // Set the pointer before the count, so that any other process trying to read 1106 // will not risk out-of-bounds access to the array. 1107 header.mAliases = ptr; 1108 header.mAliasCount.store(count); 1109 } 1110 1111 void FontList::SetLocalNames( 1112 nsTHashMap<nsCStringHashKey, LocalFaceRec::InitData>& aLocalNameTable) { 1113 MOZ_ASSERT(XRE_IsParentProcess()); 1114 Header& header = GetHeader(); 1115 if (header.mLocalFaceCount > 0) { 1116 return; // already been done! 1117 } 1118 auto faceArray = ToTArray<nsTArray<nsCString>>(aLocalNameTable.Keys()); 1119 faceArray.Sort(); 1120 size_t count = faceArray.Length(); 1121 Family* families = Families(); 1122 fontlist::Pointer ptr = Alloc(count * sizeof(LocalFaceRec)); 1123 auto* faces = ptr.ToArray<LocalFaceRec>(this, count); 1124 for (size_t i = 0; i < count; i++) { 1125 (void)new (&faces[i]) LocalFaceRec(); 1126 const auto& rec = aLocalNameTable.Get(faceArray[i]); 1127 faces[i].mKey.Assign(faceArray[i], this); 1128 // Local face name records will refer to the canonical family name; we don't 1129 // need to search aliases here. 1130 const auto* family = FindFamily(rec.mFamilyName, /*aPrimaryNameOnly*/ true); 1131 if (!family) { 1132 // Skip this record if the family was excluded by the font whitelist pref. 1133 continue; 1134 } 1135 faces[i].mFamilyIndex = family - families; 1136 if (rec.mFaceIndex == uint32_t(-1)) { 1137 // The InitData record contains an mFaceDescriptor rather than an index, 1138 // so now we need to look for the appropriate index in the family. 1139 faces[i].mFaceIndex = 0; 1140 const Pointer* faceList = 1141 static_cast<const Pointer*>(family->Faces(this)); 1142 for (uint32_t j = 0; j < family->NumFaces(); j++) { 1143 if (!faceList[j].IsNull()) { 1144 auto* f = faceList[j].ToPtr<const Face>(this); 1145 if (f && rec.mFaceDescriptor == f->mDescriptor.AsString(this)) { 1146 faces[i].mFaceIndex = j; 1147 break; 1148 } 1149 } 1150 } 1151 } else { 1152 faces[i].mFaceIndex = rec.mFaceIndex; 1153 } 1154 } 1155 header.mLocalFaces = ptr; 1156 header.mLocalFaceCount.store(count); 1157 } 1158 1159 nsCString FontList::LocalizedFamilyName(const Family* aFamily) { 1160 // If the given family was created for an alternate locale or legacy name, 1161 // search for a standard family that corresponds to it. This is a linear 1162 // search of the font list, but (a) this is only used to show names in 1163 // Preferences, so is not performance-critical for layout etc.; and (b) few 1164 // such family names are normally present anyway, the vast majority of fonts 1165 // just have a single family name and we return it directly. 1166 if (aFamily->IsAltLocaleFamily()) { 1167 // Currently only the Windows backend actually does this; on other systems, 1168 // the family index is unused and will be kNoIndex for all fonts. 1169 if (aFamily->Index() != Family::kNoIndex) { 1170 const Family* families = Families(); 1171 for (uint32_t i = 0; i < NumFamilies(); ++i) { 1172 if (families[i].Index() == aFamily->Index() && 1173 families[i].IsBundled() == aFamily->IsBundled() && 1174 !families[i].IsAltLocaleFamily()) { 1175 return families[i].DisplayName().AsString(this); 1176 } 1177 } 1178 } 1179 } 1180 1181 // For standard families (or if we failed to find the expected standard 1182 // family for some reason), just return the DisplayName. 1183 return aFamily->DisplayName().AsString(this); 1184 } 1185 1186 Family* FontList::FindFamily(const nsCString& aName, bool aPrimaryNameOnly) { 1187 struct FamilyNameComparator { 1188 FamilyNameComparator(FontList* aList, const nsCString& aTarget) 1189 : mList(aList), mTarget(aTarget) {} 1190 1191 int operator()(const Family& aVal) const { 1192 return Compare(mTarget, 1193 nsDependentCString(aVal.Key().BeginReading(mList))); 1194 } 1195 1196 private: 1197 FontList* mList; 1198 const nsCString& mTarget; 1199 }; 1200 1201 const Header& header = GetHeader(); 1202 1203 Family* families = Families(); 1204 if (!families) { 1205 return nullptr; 1206 } 1207 1208 size_t match; 1209 if (BinarySearchIf(families, 0, header.mFamilyCount, 1210 FamilyNameComparator(this, aName), &match)) { 1211 return &families[match]; 1212 } 1213 1214 if (aPrimaryNameOnly) { 1215 return nullptr; 1216 } 1217 1218 if (header.mAliasCount) { 1219 Family* aliases = AliasFamilies(); 1220 size_t match; 1221 if (aliases && BinarySearchIf(aliases, 0, header.mAliasCount, 1222 FamilyNameComparator(this, aName), &match)) { 1223 return &aliases[match]; 1224 } 1225 } 1226 1227 #ifdef XP_WIN 1228 // For Windows only, because of how DWrite munges font family names in some 1229 // cases (see 1230 // https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/02/24/90/36/WPF%20Font%20Selection%20Model.pdf 1231 // and discussion on the OpenType list), try stripping a possible style-name 1232 // suffix from the end of the requested family name. 1233 // After the deferred font loader has finished, this is no longer needed as 1234 // the "real" family names will have been found in AliasFamilies() above. 1235 if (aName.Contains(' ')) { 1236 auto pfl = gfxPlatformFontList::PlatformFontList(); 1237 pfl->mLock.AssertCurrentThreadIn(); 1238 if (header.mAliasCount) { 1239 // Aliases have been fully loaded by the parent process, so just discard 1240 // any stray mAliasTable and mLocalNameTable entries from earlier calls 1241 // to this code, and return. 1242 pfl->mAliasTable.Clear(); 1243 pfl->mLocalNameTable.Clear(); 1244 mFaceNamesRead.Clear(); 1245 return nullptr; 1246 } 1247 1248 // Do we already have an aliasData record for this name? If so, we just 1249 // return its base family. 1250 if (auto lookup = pfl->mAliasTable.Lookup(aName)) { 1251 return FindFamily(lookup.Data()->mBaseFamily, true); 1252 } 1253 1254 // Strip the style suffix (after last space in the name) to get a "base" 1255 // family name. 1256 const char* data = aName.BeginReading(); 1257 int32_t index = aName.Length(); 1258 while (--index > 0) { 1259 if (data[index] == ' ') { 1260 break; 1261 } 1262 } 1263 if (index <= 0) { 1264 return nullptr; 1265 } 1266 nsAutoCString base(Substring(aName, 0, index)); 1267 auto familyCount = header.mFamilyCount; 1268 if (BinarySearchIf(families, 0, familyCount, 1269 FamilyNameComparator(this, base), &match)) { 1270 // Check to see if we have already read the face names for this base 1271 // family. Note: EnsureLengthAtLeast will default new entries to false. 1272 mFaceNamesRead.EnsureLengthAtLeast(familyCount); 1273 if (mFaceNamesRead[match]) { 1274 return nullptr; 1275 } 1276 // This may be a possible base family to satisfy the search; call 1277 // ReadFaceNamesForFamily and see if the desired name ends up in 1278 // mAliasTable. 1279 // Note that ReadFaceNamesForFamily may store entries in mAliasTable 1280 // (and mLocalNameTable), but if this is happening in a content 1281 // process (which is the common case) those entries will not be saved 1282 // into the shared font list; they're just used here until the "real" 1283 // alias list is ready, then discarded. 1284 Family* baseFamily = &families[match]; 1285 pfl->ReadFaceNamesForFamily(baseFamily, false); 1286 mFaceNamesRead[match] = true; 1287 if (auto lookup = pfl->mAliasTable.Lookup(aName)) { 1288 if (lookup.Data()->mFaces.Length() != baseFamily->NumFaces()) { 1289 // If the alias family doesn't have all the faces of the base family, 1290 // then style matching may end up resolving to a face that isn't 1291 // supposed to be available in the legacy styled family. To ensure 1292 // such mis-styling will get fixed, we start the async font info 1293 // loader (if it hasn't yet been triggered), which will pull in the 1294 // full metadata we need and then force a reflow. 1295 pfl->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true); 1296 } 1297 return baseFamily; 1298 } 1299 } 1300 } 1301 #endif 1302 1303 return nullptr; 1304 } 1305 1306 LocalFaceRec* FontList::FindLocalFace(const nsCString& aName) { 1307 struct FaceNameComparator { 1308 FaceNameComparator(FontList* aList, const nsCString& aTarget) 1309 : mList(aList), mTarget(aTarget) {} 1310 1311 int operator()(const LocalFaceRec& aVal) const { 1312 return Compare(mTarget, 1313 nsDependentCString(aVal.mKey.BeginReading(mList))); 1314 } 1315 1316 private: 1317 FontList* mList; 1318 const nsCString& mTarget; 1319 }; 1320 1321 Header& header = GetHeader(); 1322 1323 LocalFaceRec* faces = LocalFaces(); 1324 size_t match; 1325 if (faces && BinarySearchIf(faces, 0, header.mLocalFaceCount, 1326 FaceNameComparator(this, aName), &match)) { 1327 return &faces[match]; 1328 } 1329 1330 return nullptr; 1331 } 1332 1333 void FontList::SearchForLocalFace(const nsACString& aName, Family** aFamily, 1334 Face** aFace) { 1335 Header& header = GetHeader(); 1336 MOZ_ASSERT(header.mLocalFaceCount == 0, 1337 "do not use when local face names are already set up!"); 1338 LOG_FONTLIST( 1339 ("(shared-fontlist) local face search for (%s)", aName.BeginReading())); 1340 char initial = aName[0]; 1341 Family* families = Families(); 1342 if (!families) { 1343 return; 1344 } 1345 for (uint32_t i = 0; i < header.mFamilyCount; i++) { 1346 Family* family = &families[i]; 1347 if (family->Key().BeginReading(this)[0] != initial) { 1348 continue; 1349 } 1350 LOG_FONTLIST(("(shared-fontlist) checking family (%s)", 1351 family->Key().AsString(this).BeginReading())); 1352 if (!family->IsInitialized()) { 1353 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family)) { 1354 continue; 1355 } 1356 } 1357 Pointer* faces = family->Faces(this); 1358 if (!faces) { 1359 continue; 1360 } 1361 for (uint32_t j = 0; j < family->NumFaces(); j++) { 1362 auto* face = faces[j].ToPtr<Face>(this); 1363 if (!face) { 1364 continue; 1365 } 1366 nsAutoCString psname, fullname; 1367 if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames( 1368 family, face, psname, fullname)) { 1369 LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)", 1370 psname.get(), fullname.get())); 1371 ToLowerCase(psname); 1372 ToLowerCase(fullname); 1373 if (aName == psname || aName == fullname) { 1374 *aFamily = family; 1375 *aFace = face; 1376 return; 1377 } 1378 } 1379 } 1380 } 1381 } 1382 1383 size_t FontList::SizeOfIncludingThis( 1384 mozilla::MallocSizeOf aMallocSizeOf) const { 1385 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1386 } 1387 1388 size_t FontList::SizeOfExcludingThis( 1389 mozilla::MallocSizeOf aMallocSizeOf) const { 1390 size_t result = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf); 1391 for (const auto& b : mBlocks) { 1392 result += aMallocSizeOf(b.get()); 1393 } 1394 return result; 1395 } 1396 1397 size_t FontList::AllocatedShmemSize() const { 1398 size_t result = 0; 1399 for (const auto& b : mBlocks) { 1400 result += b->BlockSize(); 1401 } 1402 return result; 1403 } 1404 1405 } // namespace fontlist 1406 } // namespace mozilla