gfxUserFontSet.cpp (52983B)
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 "mozilla/Logging.h" 7 8 #include "gfxUserFontSet.h" 9 #include "gfxPlatform.h" 10 #include "mozilla/Atomics.h" 11 #include "mozilla/FontPropertyTypes.h" 12 #include "mozilla/ProfilerLabels.h" 13 #include "mozilla/Services.h" 14 #include "mozilla/StaticPrefs_gfx.h" 15 #include "mozilla/glean/GfxMetrics.h" 16 #include "mozilla/gfx/2D.h" 17 #include "gfxPlatformFontList.h" 18 #include "mozilla/PostTraversalTask.h" 19 #include "mozilla/dom/WorkerCommon.h" 20 #include "gfxOTSUtils.h" 21 #include "nsFontFaceLoader.h" 22 #include "nsIFontLoadCompleteCallback.h" 23 #include "nsProxyRelease.h" 24 #include "nsContentUtils.h" 25 #include "nsPresContext.h" 26 #include "mozilla/dom/FontFaceSetImpl.h" 27 #include "nsTHashSet.h" 28 29 using namespace mozilla; 30 31 mozilla::LogModule* gfxUserFontSet::GetUserFontsLog() { 32 static LazyLogModule sLog("userfonts"); 33 return sLog; 34 } 35 36 #define LOG(args) \ 37 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args) 38 #define LOG_ENABLED() \ 39 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug) 40 41 static Atomic<uint64_t> sFontSetGeneration(0); 42 43 gfxUserFontEntry::gfxUserFontEntry(nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList, 44 gfxUserFontAttributes&& aAttr) 45 : gfxFontEntry("userfont"_ns), 46 mUserFontLoadState(STATUS_NOT_LOADED), 47 mFontDataLoadingState(NOT_LOADING), 48 mSeenLocalSource(false), 49 mUnsupportedFormat(false), 50 mFontDisplay(aAttr.mFontDisplay), 51 mLoader(nullptr) { 52 mIsUserFontContainer = true; 53 mSrcList = std::move(aFontFaceSrcList); 54 mCurrentSrcIndex = 0; 55 mWeightRange = aAttr.mWeight; 56 mStretchRange = aAttr.mStretch; 57 mStyleRange = aAttr.mStyle; 58 mFeatureSettings = std::move(aAttr.mFeatureSettings); 59 mVariationSettings = std::move(aAttr.mVariationSettings); 60 mLanguageOverride = aAttr.mLanguageOverride; 61 SetUnicodeRangeMap(std::move(aAttr.mUnicodeRanges)); 62 mRangeFlags = aAttr.mRangeFlags; 63 mAscentOverride = aAttr.mAscentOverride; 64 mDescentOverride = aAttr.mDescentOverride; 65 mLineGapOverride = aAttr.mLineGapOverride; 66 mSizeAdjust = aAttr.mSizeAdjust; 67 mFamilyName = aAttr.mFamilyName; 68 } 69 70 void gfxUserFontEntry::UpdateAttributes(gfxUserFontAttributes&& aAttr) { 71 MOZ_ASSERT(NS_IsMainThread()); 72 73 // Remove the entry from the user font cache, if present there, as the cache 74 // key may no longer be correct with the new attributes. 75 gfxUserFontSet::UserFontCache::ForgetFont(this); 76 77 mFontDisplay = aAttr.mFontDisplay; 78 mWeightRange = aAttr.mWeight; 79 mStretchRange = aAttr.mStretch; 80 mStyleRange = aAttr.mStyle; 81 mFeatureSettings = std::move(aAttr.mFeatureSettings); 82 mVariationSettings = std::move(aAttr.mVariationSettings); 83 mLanguageOverride = aAttr.mLanguageOverride; 84 SetUnicodeRangeMap(std::move(aAttr.mUnicodeRanges)); 85 mRangeFlags = aAttr.mRangeFlags; 86 mAscentOverride = aAttr.mAscentOverride; 87 mDescentOverride = aAttr.mDescentOverride; 88 mLineGapOverride = aAttr.mLineGapOverride; 89 mSizeAdjust = aAttr.mSizeAdjust; 90 } 91 92 gfxUserFontEntry::~gfxUserFontEntry() { 93 // Assert that we don't drop any gfxUserFontEntry objects during a Servo 94 // traversal, since PostTraversalTask objects can hold raw pointers to 95 // gfxUserFontEntry objects. 96 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); 97 } 98 99 bool gfxUserFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 100 const gfxUserFontAttributes& aAttr) { 101 return mWeightRange == aAttr.mWeight && mStretchRange == aAttr.mStretch && 102 mStyleRange == aAttr.mStyle && 103 mFeatureSettings == aAttr.mFeatureSettings && 104 mVariationSettings == aAttr.mVariationSettings && 105 mLanguageOverride == aAttr.mLanguageOverride && 106 mSrcList == aFontFaceSrcList && mFontDisplay == aAttr.mFontDisplay && 107 mRangeFlags == aAttr.mRangeFlags && 108 mAscentOverride == aAttr.mAscentOverride && 109 mDescentOverride == aAttr.mDescentOverride && 110 mLineGapOverride == aAttr.mLineGapOverride && 111 mSizeAdjust == aAttr.mSizeAdjust && 112 ((!aAttr.mUnicodeRanges && !mCharacterMap) || 113 (aAttr.mUnicodeRanges && mCharacterMap && 114 GetCharacterMap()->Equals(aAttr.mUnicodeRanges))); 115 } 116 117 gfxFont* gfxUserFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) { 118 MOZ_ASSERT_UNREACHABLE( 119 "should only be creating a gfxFont" 120 " with an actual platform font entry"); 121 122 // userfont entry is a container, can't create font from the container 123 return nullptr; 124 } 125 126 class MOZ_STACK_CLASS gfxOTSMessageContext : public gfxOTSContext { 127 public: 128 virtual ~gfxOTSMessageContext() { 129 MOZ_ASSERT(mMessages.IsEmpty(), "should have called TakeMessages"); 130 } 131 132 virtual void Message(int level, const char* format, 133 ...) MSGFUNC_FMT_ATTR override { 134 va_list va; 135 136 // Special-case glyph bounding box warnings: collect all bad glyph IDs, 137 // so we can issue a single message at the end. 138 if (level > 0 && strstr(format, "bbox was incorrect")) { 139 // Extract the glyph ID from the message: it follows the last space in 140 // the message string. 141 const char* lastSpace = strrchr(format, ' '); 142 if (lastSpace) { 143 int gid = atoi(lastSpace + 1); 144 mBadBBoxGlyphs.AppendElement(gid); 145 } 146 return; 147 } 148 149 va_start(va, format); 150 151 nsCString msg; 152 msg.AppendVprintf(format, va); 153 154 va_end(va); 155 156 if (level > 0) { 157 // For warnings (rather than errors that cause the font to fail), 158 // we only report the first instance of any given message. 159 if (!mWarningsIssued.EnsureInserted(msg)) { 160 return; 161 } 162 } 163 164 mMessages.AppendElement(gfxUserFontEntry::OTSMessage{msg, level}); 165 } 166 167 bool Process(ots::OTSStream* aOutput, const uint8_t* aInput, size_t aLength, 168 nsTArray<gfxUserFontEntry::OTSMessage>& aMessages) { 169 bool ok = ots::OTSContext::Process(aOutput, aInput, aLength); 170 aMessages = TakeMessages(); 171 return ok; 172 } 173 174 nsTArray<gfxUserFontEntry::OTSMessage>&& TakeMessages() { 175 if (!mBadBBoxGlyphs.IsEmpty()) { 176 nsAutoCString msg("Glyph bbox was incorrect (glyph ids"); 177 for (const auto gid : mBadBBoxGlyphs) { 178 msg.Append(" "); 179 msg.AppendInt(gid); 180 } 181 msg.Append(")"); 182 mMessages.AppendElement(gfxUserFontEntry::OTSMessage{msg, 1}); 183 mBadBBoxGlyphs.Clear(); 184 } 185 return std::move(mMessages); 186 } 187 188 private: 189 nsTHashSet<nsCString> mWarningsIssued; 190 nsTArray<gfxUserFontEntry::OTSMessage> mMessages; 191 nsTArray<uint16_t> mBadBBoxGlyphs; 192 }; 193 194 // Call the OTS library to sanitize an sfnt before attempting to use it. 195 // Returns a newly-allocated block, or nullptr in case of fatal errors. 196 const uint8_t* gfxUserFontEntry::SanitizeOpenTypeData( 197 const uint8_t* aData, uint32_t aLength, uint32_t& aSanitaryLength, 198 gfxUserFontType& aFontType, nsTArray<OTSMessage>& aMessages) { 199 aFontType = gfxFontUtils::DetermineFontDataType(aData, aLength); 200 glean::webfont::fonttype.AccumulateSingleSample(uint32_t(aFontType)); 201 202 size_t lengthHint = gfxOTSContext::GuessSanitizedFontSize(aLength, aFontType); 203 if (!lengthHint) { 204 aSanitaryLength = 0; 205 return nullptr; 206 } 207 208 gfxOTSExpandingMemoryStream<gfxOTSMozAlloc> output(lengthHint); 209 210 gfxOTSMessageContext otsContext; 211 if (!otsContext.Process(&output, aData, aLength, aMessages)) { 212 // Failed to decode/sanitize the font, so discard it. 213 aSanitaryLength = 0; 214 return nullptr; 215 } 216 217 aSanitaryLength = output.Tell(); 218 return static_cast<const uint8_t*>(output.forget()); 219 } 220 221 void gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry, 222 uint32_t aSrcIndex, bool aPrivate, 223 const nsACString& aOriginalName, 224 FallibleTArray<uint8_t>* aMetadata, 225 uint32_t aMetaOrigLen, 226 uint8_t aCompression) { 227 if (!aFontEntry->mUserFontData) { 228 aFontEntry->mUserFontData = MakeUnique<gfxUserFontData>(); 229 } 230 gfxUserFontData* userFontData = aFontEntry->mUserFontData.get(); 231 userFontData->mSrcIndex = aSrcIndex; 232 const gfxFontFaceSrc& src = mSrcList[aSrcIndex]; 233 switch (src.mSourceType) { 234 case gfxFontFaceSrc::eSourceType_Local: 235 userFontData->mLocalName = src.mLocalName; 236 break; 237 case gfxFontFaceSrc::eSourceType_URL: 238 userFontData->mURI = src.mURI; 239 userFontData->mPrincipal = mPrincipal; 240 break; 241 case gfxFontFaceSrc::eSourceType_Buffer: 242 userFontData->mIsBuffer = true; 243 break; 244 } 245 userFontData->mPrivate = aPrivate; 246 userFontData->mTechFlags = src.mTechFlags; 247 userFontData->mFormatHint = src.mFormatHint; 248 userFontData->mRealName = aOriginalName; 249 if (aMetadata) { 250 userFontData->mMetadata = std::move(*aMetadata); 251 userFontData->mMetaOrigLen = aMetaOrigLen; 252 userFontData->mCompression = aCompression; 253 } 254 } 255 256 size_t gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 257 return aMallocSizeOf(this) + 258 mMetadata.ShallowSizeOfExcludingThis(aMallocSizeOf) + 259 mLocalName.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + 260 mRealName.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 261 // Not counting mURI and mPrincipal, as those will be shared. 262 } 263 264 /*virtual*/ 265 gfxUserFontFamily::~gfxUserFontFamily() { 266 // Should not be dropped by stylo 267 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); 268 } 269 270 already_AddRefed<gfxFontSrcPrincipal> gfxFontFaceSrc::LoadPrincipal( 271 const gfxUserFontSet& aFontSet) const { 272 MOZ_ASSERT(mSourceType == eSourceType_URL); 273 if (mUseOriginPrincipal) { 274 MOZ_ASSERT(mOriginPrincipal); 275 return RefPtr{mOriginPrincipal}.forget(); 276 } 277 return aFontSet.GetStandardFontLoadPrincipal(); 278 } 279 280 void gfxUserFontEntry::GetFamilyNameAndURIForLogging(uint32_t aSrcIndex, 281 nsACString& aFamilyName, 282 nsACString& aURI) { 283 aFamilyName = mFamilyName; 284 285 aURI.Truncate(); 286 if (aSrcIndex >= mSrcList.Length()) { 287 aURI.AppendLiteral("(end of source list)"); 288 } else { 289 if (mSrcList[aSrcIndex].mURI) { 290 mSrcList[aSrcIndex].mURI->GetSpec(aURI); 291 // If the source URI was very long, elide the middle of it. 292 // In principle, the byte-oriented chopping here could leave us 293 // with partial UTF-8 characters at the point where we cut it, 294 // but it really doesn't matter as this is just for logging. 295 const uint32_t kMaxURILengthForLogging = 256; 296 // UTF-8 ellipsis, with spaces to allow additional wrap opportunities 297 // in the resulting log message 298 const char kEllipsis[] = {' ', '\xE2', '\x80', '\xA6', ' '}; 299 if (aURI.Length() > kMaxURILengthForLogging) { 300 aURI.Replace(kMaxURILengthForLogging / 2, 301 aURI.Length() - kMaxURILengthForLogging, kEllipsis, 302 std::size(kEllipsis)); 303 } 304 } else { 305 aURI.AppendLiteral("(invalid URI)"); 306 } 307 } 308 } 309 310 struct WOFFHeader { 311 AutoSwap_PRUint32 signature; 312 AutoSwap_PRUint32 flavor; 313 AutoSwap_PRUint32 length; 314 AutoSwap_PRUint16 numTables; 315 AutoSwap_PRUint16 reserved; 316 AutoSwap_PRUint32 totalSfntSize; 317 AutoSwap_PRUint16 majorVersion; 318 AutoSwap_PRUint16 minorVersion; 319 AutoSwap_PRUint32 metaOffset; 320 AutoSwap_PRUint32 metaCompLen; 321 AutoSwap_PRUint32 metaOrigLen; 322 AutoSwap_PRUint32 privOffset; 323 AutoSwap_PRUint32 privLen; 324 }; 325 326 struct WOFF2Header { 327 AutoSwap_PRUint32 signature; 328 AutoSwap_PRUint32 flavor; 329 AutoSwap_PRUint32 length; 330 AutoSwap_PRUint16 numTables; 331 AutoSwap_PRUint16 reserved; 332 AutoSwap_PRUint32 totalSfntSize; 333 AutoSwap_PRUint32 totalCompressedSize; 334 AutoSwap_PRUint16 majorVersion; 335 AutoSwap_PRUint16 minorVersion; 336 AutoSwap_PRUint32 metaOffset; 337 AutoSwap_PRUint32 metaCompLen; 338 AutoSwap_PRUint32 metaOrigLen; 339 AutoSwap_PRUint32 privOffset; 340 AutoSwap_PRUint32 privLen; 341 }; 342 343 template <typename HeaderT> 344 void CopyWOFFMetadata(const uint8_t* aFontData, uint32_t aLength, 345 FallibleTArray<uint8_t>* aMetadata, 346 uint32_t* aMetaOrigLen) { 347 // This function may be called with arbitrary, unvalidated "font" data 348 // from @font-face, so it needs to be careful to bounds-check, etc., 349 // before trying to read anything. 350 // This just saves a copy of the compressed data block; it does NOT check 351 // that the block can be successfully decompressed, or that it contains 352 // well-formed/valid XML metadata. 353 if (aLength < sizeof(HeaderT)) { 354 return; 355 } 356 const HeaderT* woff = reinterpret_cast<const HeaderT*>(aFontData); 357 uint32_t metaOffset = woff->metaOffset; 358 uint32_t metaCompLen = woff->metaCompLen; 359 if (!metaOffset || !metaCompLen || !woff->metaOrigLen) { 360 return; 361 } 362 if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) { 363 return; 364 } 365 if (!aMetadata->SetLength(woff->metaCompLen, fallible)) { 366 return; 367 } 368 memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen); 369 *aMetaOrigLen = woff->metaOrigLen; 370 } 371 372 void gfxUserFontEntry::LoadNextSrc() { 373 NS_ASSERTION(mCurrentSrcIndex < mSrcList.Length(), 374 "already at the end of the src list for user font"); 375 NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED || 376 mUserFontLoadState == STATUS_LOAD_PENDING || 377 mUserFontLoadState == STATUS_LOADING) && 378 mFontDataLoadingState < LOADING_FAILED, 379 "attempting to load a font that has either completed or failed"); 380 381 if (mUserFontLoadState == STATUS_NOT_LOADED) { 382 SetLoadState(STATUS_LOADING); 383 mFontDataLoadingState = LOADING_STARTED; 384 mUnsupportedFormat = false; 385 } else { 386 // we were already loading; move to the next source, 387 // but don't reset state - if we've already timed out, 388 // that counts against the new download 389 mCurrentSrcIndex++; 390 } 391 392 DoLoadNextSrc(false); 393 } 394 395 void gfxUserFontEntry::FontLoadComplete() { 396 AutoTArray<RefPtr<gfxUserFontSet>, 4> fontSets; 397 GetUserFontSets(fontSets); 398 for (gfxUserFontSet* fontSet : fontSets) { 399 fontSet->IncrementGeneration(); 400 if (FontVisibilityProvider* ctx = 401 dom::FontFaceSetImpl::GetFontVisibilityProviderFor(fontSet)) { 402 // Update layout for the presence of the new font. Since this is 403 // asynchronous, reflows will coalesce. 404 ctx->UserFontSetUpdated(this); 405 LOG(("userfonts (%p) reflow for pres context %p\n", this, ctx)); 406 } 407 } 408 } 409 410 void gfxUserFontEntry::ContinueLoad() { 411 if (mUserFontLoadState == STATUS_NOT_LOADED) { 412 // We must have been cancelled (possibly due to a font-list refresh) while 413 // the runnable was pending, so just bail out. 414 return; 415 } 416 417 MOZ_ASSERT(mUserFontLoadState == STATUS_LOAD_PENDING); 418 MOZ_ASSERT(mSrcList[mCurrentSrcIndex].mSourceType == 419 gfxFontFaceSrc::eSourceType_URL); 420 421 SetLoadState(STATUS_LOADING); 422 DoLoadNextSrc(/* aIsContinue = */ true); 423 if (LoadState() != STATUS_LOADING) { 424 MOZ_ASSERT(mUserFontLoadState != STATUS_LOAD_PENDING, 425 "Not in parallel traversal, shouldn't get LOAD_PENDING again"); 426 // Loading is synchronously finished (loaded from cache or failed). We 427 // need to increment the generation so that we flush the style data to 428 // use the new loaded font face. 429 FontLoadComplete(); 430 } 431 } 432 433 static bool IgnorePrincipal(gfxFontSrcURI* aURI) { 434 return aURI->InheritsSecurityContext(); 435 } 436 437 void gfxUserFontEntry::DoLoadNextSrc(bool aIsContinue) { 438 RefPtr<gfxUserFontSet> fontSet = GetUserFontSet(); 439 if (NS_WARN_IF(!fontSet)) { 440 LOG(("userfonts (%p) failed expired font set for (%s)\n", fontSet.get(), 441 mFamilyName.get())); 442 mFontDataLoadingState = LOADING_FAILED; 443 SetLoadState(STATUS_FAILED); 444 return; 445 } 446 447 uint32_t numSrc = mSrcList.Length(); 448 449 // load each src entry in turn, until a local face is found 450 // or a download begins successfully 451 while (mCurrentSrcIndex < numSrc) { 452 gfxFontFaceSrc& currSrc = mSrcList[mCurrentSrcIndex]; 453 454 // src local ==> lookup and load immediately 455 456 if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) { 457 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 458 pfl->AddUserFontSet(fontSet); 459 // Don't look up local fonts if the font whitelist is being used. 460 gfxFontEntry* fe = nullptr; 461 if (!pfl->IsFontFamilyWhitelistActive()) { 462 fe = gfxPlatform::GetPlatform()->LookupLocalFont( 463 fontSet->GetFontVisibilityProvider(), currSrc.mLocalName, Weight(), 464 Stretch(), SlantStyle()); 465 // Note that we've attempted a local lookup, even if it failed, 466 // as this means we are dependent on any updates to the font list. 467 mSeenLocalSource = true; 468 nsTArray<RefPtr<gfxUserFontSet>> fontSets; 469 GetUserFontSets(fontSets); 470 for (gfxUserFontSet* fontSet : fontSets) { 471 // We need to note on each gfxUserFontSet that contains the user 472 // font entry that we used a local() rule. 473 fontSet->SetLocalRulesUsed(); 474 } 475 } 476 if (fe) { 477 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n", 478 fontSet.get(), mCurrentSrcIndex, currSrc.mLocalName.get(), 479 mFamilyName.get(), uint32_t(fontSet->GetGeneration()))); 480 fe->mFeatureSettings.AppendElements(mFeatureSettings); 481 fe->mVariationSettings.AppendElements(mVariationSettings); 482 fe->mLanguageOverride = mLanguageOverride; 483 fe->mFamilyName = mFamilyName; 484 fe->mRangeFlags = mRangeFlags; 485 fe->mAscentOverride = mAscentOverride; 486 fe->mDescentOverride = mDescentOverride; 487 fe->mLineGapOverride = mLineGapOverride; 488 fe->mSizeAdjust = mSizeAdjust; 489 // For src:local(), we don't care whether the request is from 490 // a private window as there's no issue of caching resources; 491 // local fonts are just available all the time. 492 StoreUserFontData(fe, mCurrentSrcIndex, false, nsCString(), nullptr, 0, 493 gfxUserFontData::kUnknownCompression); 494 mPlatformFontEntry = fe; 495 SetLoadState(STATUS_LOADED); 496 glean::webfont::srctype.AccumulateSingleSample(currSrc.mSourceType + 1); 497 return; 498 } 499 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", 500 fontSet.get(), mCurrentSrcIndex, currSrc.mLocalName.get(), 501 mFamilyName.get())); 502 } 503 504 // src url ==> start the load process 505 else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) { 506 if (gfxPlatform::GetPlatform()->IsFontFormatSupported( 507 currSrc.mFormatHint, currSrc.mTechFlags)) { 508 // TODO(emilio): Make UserFontCache thread-safe maybe? But we need to 509 // potentially do CSP checks so maybe not trivial. 510 const bool canCheckCache = [&] { 511 if (NS_IsMainThread()) { 512 return true; 513 } 514 if (gfxFontUtils::CurrentServoStyleSet()) { 515 // Only support style worker threads synchronously getting entries 516 // from the font cache when it's not a data: URI @font-face that 517 // came from UA or user sheets, since we were not able to call 518 // IsFontLoadAllowed ahead of time for these entries. 519 return !currSrc.mUseOriginPrincipal || 520 !IgnorePrincipal(currSrc.mURI); 521 } 522 return false; 523 }(); 524 525 // see if we have an existing entry for this source 526 if (canCheckCache) { 527 gfxFontEntry* fe = 528 gfxUserFontSet::UserFontCache::GetFont(currSrc, *this); 529 if (fe) { 530 mPlatformFontEntry = fe; 531 SetLoadState(STATUS_LOADED); 532 LOG( 533 ("userfonts (%p) [src %d] " 534 "loaded uri from cache: (%s) for (%s)\n", 535 fontSet.get(), mCurrentSrcIndex, 536 currSrc.mURI->GetSpecOrDefault().get(), mFamilyName.get())); 537 return; 538 } 539 } 540 541 if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) { 542 // If we need to start a font load and we're on a style 543 // worker thread, we have to defer it. 544 SetLoadState(STATUS_LOAD_PENDING); 545 set->AppendTask(PostTraversalTask::LoadFontEntry(this)); 546 return; 547 } 548 549 if (dom::IsCurrentThreadRunningWorker()) { 550 // TODO: Maybe support loading the font entry in workers, at least for 551 // buffers or other sync sources? 552 SetLoadState(STATUS_LOAD_PENDING); 553 NS_DispatchToMainThread( 554 NewRunnableMethod("gfxUserFontEntry::ContinueLoad", this, 555 &gfxUserFontEntry::ContinueLoad)); 556 return; 557 } 558 559 // record the principal we should use for the load for use when 560 // creating a channel and when caching the loaded entry. 561 mPrincipal = currSrc.LoadPrincipal(*fontSet); 562 563 const bool loadDoesntSpin = 564 !aIsContinue && currSrc.mURI->SyncLoadIsOK(); 565 if (loadDoesntSpin) { 566 uint8_t* buffer = nullptr; 567 uint32_t bufferLength = 0; 568 569 // sync load font immediately 570 nsresult rv = 571 fontSet->SyncLoadFontData(this, &currSrc, buffer, bufferLength); 572 573 if (NS_SUCCEEDED(rv) && 574 LoadPlatformFontSync(mCurrentSrcIndex, buffer, bufferLength)) { 575 SetLoadState(STATUS_LOADED); 576 glean::webfont::srctype.AccumulateSingleSample(currSrc.mSourceType + 577 1); 578 return; 579 } 580 fontSet->LogMessage(this, mCurrentSrcIndex, "font load failed", 581 nsIScriptError::errorFlag, rv); 582 } else if (!aIsContinue) { 583 RefPtr<nsIRunnable> runnable = 584 NewRunnableMethod("gfxUserFontEntry::ContinueLoad", this, 585 &gfxUserFontEntry::ContinueLoad); 586 SetLoadState(STATUS_LOAD_PENDING); 587 // We don't want to trigger the channel open at random points in 588 // time, because it can run privileged JS. 589 if (!nsContentUtils::IsSafeToRunScript()) { 590 // There's a script-blocker on the stack. We know the sooner point 591 // where we can trigger the load. 592 nsContentUtils::AddScriptRunner(runnable.forget()); 593 } else { 594 // We dispatch with a rather high priority, since somebody actually 595 // cares about this font. 596 NS_DispatchToCurrentThreadQueue(runnable.forget(), 597 EventQueuePriority::MediumHigh); 598 } 599 return; 600 } else { 601 // Actually start the async load. 602 nsresult rv = fontSet->StartLoad(this, mCurrentSrcIndex); 603 if (NS_SUCCEEDED(rv)) { 604 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n", 605 fontSet.get(), mCurrentSrcIndex, 606 currSrc.mURI->GetSpecOrDefault().get(), mFamilyName.get())); 607 return; 608 } 609 fontSet->LogMessage(this, mCurrentSrcIndex, 610 "failed to start download", 611 nsIScriptError::errorFlag, rv); 612 } 613 } else { 614 // We don't log a warning to the web console yet, 615 // as another source may load successfully 616 mUnsupportedFormat = true; 617 } 618 } else { 619 // FontFace buffer ==> load immediately 620 MOZ_ASSERT(currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Buffer); 621 622 uint8_t* buffer = nullptr; 623 uint32_t bufferLength = 0; 624 625 // sync load font immediately 626 currSrc.mBuffer->TakeBuffer(buffer, bufferLength); 627 if (buffer && 628 LoadPlatformFontSync(mCurrentSrcIndex, buffer, bufferLength)) { 629 // LoadPlatformFontSync takes ownership of the buffer, so no need 630 // to free it here. 631 SetLoadState(STATUS_LOADED); 632 glean::webfont::srctype.AccumulateSingleSample(currSrc.mSourceType + 1); 633 return; 634 } 635 fontSet->LogMessage(this, mCurrentSrcIndex, "font load failed", 636 nsIScriptError::errorFlag); 637 } 638 639 mCurrentSrcIndex++; 640 } 641 642 if (mUnsupportedFormat) { 643 fontSet->LogMessage(this, mCurrentSrcIndex, "no supported format found", 644 nsIScriptError::warningFlag); 645 } 646 647 // all src's failed; mark this entry as unusable (so fallback will occur) 648 LOG(("userfonts (%p) failed all src for (%s)\n", fontSet.get(), 649 mFamilyName.get())); 650 mFontDataLoadingState = LOADING_FAILED; 651 SetLoadState(STATUS_FAILED); 652 } 653 654 void gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState) { 655 mUserFontLoadState = aLoadState; 656 } 657 658 bool gfxUserFontEntry::LoadPlatformFontSync(uint32_t aSrcIndex, 659 const uint8_t* aFontData, 660 uint32_t aLength) { 661 AUTO_PROFILER_LABEL("gfxUserFontEntry::LoadPlatformFontSync", OTHER); 662 NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED || 663 mUserFontLoadState == STATUS_LOAD_PENDING || 664 mUserFontLoadState == STATUS_LOADING) && 665 mFontDataLoadingState < LOADING_FAILED, 666 "attempting to load a font that has either completed or failed"); 667 668 // Unwrap/decompress/sanitize or otherwise munge the downloaded data 669 // to make a usable sfnt structure. 670 671 // Call the OTS sanitizer; this will also decode WOFF to sfnt 672 // if necessary. The original data in aFontData is left unchanged. 673 uint32_t sanitaryLen; 674 gfxUserFontType fontType; 675 nsTArray<OTSMessage> messages; 676 const uint8_t* sanitaryData = 677 SanitizeOpenTypeData(aFontData, aLength, sanitaryLen, fontType, messages); 678 679 return LoadPlatformFont(aSrcIndex, aFontData, aLength, fontType, sanitaryData, 680 sanitaryLen, std::move(messages)); 681 } 682 683 void gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread( 684 uint32_t aSrcIndex, const uint8_t* aFontData, uint32_t aLength, 685 nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> aCallback) { 686 MOZ_ASSERT(!NS_IsMainThread()); 687 688 uint32_t sanitaryLen; 689 gfxUserFontType fontType; 690 nsTArray<OTSMessage> messages; 691 const uint8_t* sanitaryData = 692 SanitizeOpenTypeData(aFontData, aLength, sanitaryLen, fontType, messages); 693 694 nsCOMPtr<nsIRunnable> event = 695 NewRunnableMethod<uint32_t, const uint8_t*, uint32_t, gfxUserFontType, 696 const uint8_t*, uint32_t, nsTArray<OTSMessage>&&, 697 nsMainThreadPtrHandle<nsIFontLoadCompleteCallback>>( 698 "gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread", this, 699 &gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread, aSrcIndex, 700 aFontData, aLength, fontType, sanitaryData, sanitaryLen, 701 std::move(messages), aCallback); 702 NS_DispatchToMainThread(event.forget()); 703 } 704 705 bool gfxUserFontEntry::LoadPlatformFont(uint32_t aSrcIndex, 706 const uint8_t* aOriginalFontData, 707 uint32_t aOriginalLength, 708 gfxUserFontType aFontType, 709 const uint8_t* aSanitizedFontData, 710 uint32_t aSanitizedLength, 711 nsTArray<OTSMessage>&& aMessages) { 712 RefPtr<gfxUserFontSet> fontSet = GetUserFontSet(); 713 if (NS_WARN_IF(!fontSet)) { 714 free((void*)aOriginalFontData); 715 free((void*)aSanitizedFontData); 716 return false; 717 } 718 719 for (const auto& msg : aMessages) { 720 fontSet->LogMessage(this, aSrcIndex, msg.mMessage.get(), 721 msg.mLevel > 0 ? nsIScriptError::warningFlag 722 : nsIScriptError::errorFlag); 723 } 724 725 if (!aSanitizedFontData) { 726 fontSet->LogMessage(this, aSrcIndex, "rejected by sanitizer"); 727 } else { 728 // Check whether aSanitizedFontData is a known OpenType format; it might be 729 // a TrueType Collection, which OTS would accept but we don't yet 730 // know how to handle. If so, discard. 731 if (gfxFontUtils::DetermineFontDataType( 732 aSanitizedFontData, aSanitizedLength) != GFX_USERFONT_OPENTYPE) { 733 fontSet->LogMessage(this, aSrcIndex, "not a supported OpenType format"); 734 free((void*)aSanitizedFontData); 735 aSanitizedFontData = nullptr; 736 } 737 } 738 739 // Because platform font activation code may replace the name table 740 // in the font with a synthetic one, we save the original name so that 741 // it can be reported via the InspectorUtils API. 742 nsAutoCString originalFullName; 743 744 gfxFontEntry* fe = nullptr; 745 uint32_t fontCompressionRatio = 0; 746 747 if (aSanitizedFontData) { 748 if (aSanitizedLength) { 749 fontCompressionRatio = 750 uint32_t(100.0 * aOriginalLength / aSanitizedLength + 0.5); 751 if (aFontType == GFX_USERFONT_WOFF) { 752 glean::webfont::compression_woff.AccumulateSingleSample( 753 fontCompressionRatio); 754 } else if (aFontType == GFX_USERFONT_WOFF2) { 755 glean::webfont::compression_woff2.AccumulateSingleSample( 756 fontCompressionRatio); 757 } 758 } 759 760 // The sanitizer ensures that we have a valid sfnt and a usable 761 // name table, so this should never fail unless we're out of 762 // memory, and GetFullNameFromSFNT is not directly exposed to 763 // arbitrary/malicious data from the web. 764 gfxFontUtils::GetFullNameFromSFNT(aSanitizedFontData, aSanitizedLength, 765 originalFullName); 766 767 // Here ownership of aSanitizedFontData is passed to the platform, 768 // which will delete it when no longer required 769 fe = gfxPlatform::GetPlatform()->MakePlatformFont( 770 mName, Weight(), Stretch(), SlantStyle(), aSanitizedFontData, 771 aSanitizedLength); 772 if (!fe) { 773 fontSet->LogMessage(this, aSrcIndex, "not usable by platform"); 774 } 775 } 776 777 if (fe) { 778 // Save a copy of the metadata block (if present) for InspectorUtils 779 // to use if required. Ownership of the metadata block will be passed 780 // to the gfxUserFontData record below. 781 FallibleTArray<uint8_t> metadata; 782 uint32_t metaOrigLen = 0; 783 uint8_t compression = gfxUserFontData::kUnknownCompression; 784 if (aFontType == GFX_USERFONT_WOFF) { 785 CopyWOFFMetadata<WOFFHeader>(aOriginalFontData, aOriginalLength, 786 &metadata, &metaOrigLen); 787 compression = gfxUserFontData::kZlibCompression; 788 } else if (aFontType == GFX_USERFONT_WOFF2) { 789 CopyWOFFMetadata<WOFF2Header>(aOriginalFontData, aOriginalLength, 790 &metadata, &metaOrigLen); 791 compression = gfxUserFontData::kBrotliCompression; 792 } 793 794 // copy OpenType feature/language settings from the userfont entry to the 795 // newly-created font entry 796 fe->mFeatureSettings.AppendElements(mFeatureSettings); 797 fe->mVariationSettings.AppendElements(mVariationSettings); 798 fe->mLanguageOverride = mLanguageOverride; 799 fe->mFamilyName = mFamilyName; 800 fe->mRangeFlags = mRangeFlags; 801 fe->mAscentOverride = mAscentOverride; 802 fe->mDescentOverride = mDescentOverride; 803 fe->mLineGapOverride = mLineGapOverride; 804 fe->mSizeAdjust = mSizeAdjust; 805 StoreUserFontData(fe, aSrcIndex, fontSet->GetPrivateBrowsing(), 806 originalFullName, &metadata, metaOrigLen, compression); 807 LOG( 808 ("userfonts (%p) [src %d] loaded uri: (%s) for (%s) " 809 "(%p) gen: %8.8x compress: %d%%\n", 810 fontSet.get(), aSrcIndex, 811 mSrcList[aSrcIndex].mURI->GetSpecOrDefault().get(), mFamilyName.get(), 812 this, uint32_t(fontSet->GetGeneration()), fontCompressionRatio)); 813 mPlatformFontEntry = fe; 814 SetLoadState(STATUS_LOADED); 815 if (NS_IsMainThread()) { 816 // UserFontCache::CacheFont is not currently safe to call off-main-thread, 817 // so we only cache the font if this is a main-thread load. 818 gfxUserFontSet::UserFontCache::CacheFont(fe); 819 } 820 } else { 821 LOG(( 822 "userfonts (%p) [src %d] failed uri: (%s) for (%s)" 823 " error making platform font\n", 824 fontSet.get(), aSrcIndex, 825 mSrcList[aSrcIndex].mURI->GetSpecOrDefault().get(), mFamilyName.get())); 826 } 827 828 // The downloaded data can now be discarded; the font entry is using the 829 // sanitized copy 830 free((void*)aOriginalFontData); 831 832 return fe != nullptr; 833 } 834 835 void gfxUserFontEntry::Load() { 836 if (mUserFontLoadState != STATUS_NOT_LOADED) { 837 return; 838 } 839 LoadNextSrc(); 840 } 841 842 // This is called when a font download finishes. 843 // Ownership of aFontData passes in here, and the font set must 844 // ensure that it is eventually deleted via free(). 845 void gfxUserFontEntry::FontDataDownloadComplete( 846 uint32_t aSrcIndex, const uint8_t* aFontData, uint32_t aLength, 847 nsresult aDownloadStatus, nsIFontLoadCompleteCallback* aCallback) { 848 MOZ_ASSERT(NS_IsMainThread()); 849 850 // forget about the loader, as we no longer potentially need to cancel it 851 // if the entry is obsoleted 852 mLoader = nullptr; 853 854 // download successful, make platform font using font data 855 if (NS_SUCCEEDED(aDownloadStatus) && 856 mFontDataLoadingState != LOADING_TIMED_OUT) { 857 LoadPlatformFontAsync(aSrcIndex, aFontData, aLength, aCallback); 858 return; 859 } 860 861 RefPtr<gfxUserFontSet> fontSet = GetUserFontSet(); 862 if (fontSet) { 863 // download failed or font-display timeout passed 864 if (mFontDataLoadingState == LOADING_TIMED_OUT) { 865 fontSet->LogMessage(this, aSrcIndex, 866 "font-display timeout, webfont not used", 867 nsIScriptError::infoFlag, aDownloadStatus); 868 } else { 869 fontSet->LogMessage(this, aSrcIndex, "download failed", 870 nsIScriptError::errorFlag, aDownloadStatus); 871 } 872 } 873 874 if (aFontData) { 875 free((void*)aFontData); 876 } 877 878 FontLoadFailed(aCallback); 879 } 880 881 void gfxUserFontEntry::LoadPlatformFontAsync( 882 uint32_t aSrcIndex, const uint8_t* aFontData, uint32_t aLength, 883 nsIFontLoadCompleteCallback* aCallback) { 884 nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> cb( 885 new nsMainThreadPtrHolder<nsIFontLoadCompleteCallback>("FontLoader", 886 aCallback)); 887 888 // Do the OpenType sanitization over on the font loading thread. Once that is 889 // complete, we'll continue in ContinuePlatformFontLoadOnMainThread. 890 // 891 // We hold a strong reference to the gfxUserFontSet during this work, since 892 // the document might be closed while we are OMT, and release it at the end 893 // of ContinuePlatformFontLoadOnMainThread. 894 // 895 // If the set has already been freed, then the loading will fail when we 896 // resume on the main thread. 897 898 MOZ_ASSERT(!mLoadingFontSet); 899 mLoadingFontSet = GetUserFontSet(); 900 901 nsCOMPtr<nsIRunnable> event = 902 NewRunnableMethod<uint32_t, const uint8_t*, uint32_t, 903 nsMainThreadPtrHandle<nsIFontLoadCompleteCallback>>( 904 "gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread", this, 905 &gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread, aSrcIndex, 906 aFontData, aLength, cb); 907 MOZ_ALWAYS_SUCCEEDS(NS_DispatchBackgroundTask(event.forget())); 908 } 909 910 void gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread( 911 uint32_t aSrcIndex, const uint8_t* aOriginalFontData, 912 uint32_t aOriginalLength, gfxUserFontType aFontType, 913 const uint8_t* aSanitizedFontData, uint32_t aSanitizedLength, 914 nsTArray<OTSMessage>&& aMessages, 915 nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> aCallback) { 916 MOZ_ASSERT(NS_IsMainThread()); 917 918 bool loaded = LoadPlatformFont(aSrcIndex, aOriginalFontData, aOriginalLength, 919 aFontType, aSanitizedFontData, 920 aSanitizedLength, std::move(aMessages)); 921 aOriginalFontData = nullptr; 922 aSanitizedFontData = nullptr; 923 924 if (loaded) { 925 aCallback->FontLoadComplete(); 926 } else { 927 FontLoadFailed(aCallback); 928 } 929 930 // Set in LoadPlatformFontAsync. If it is null, then the font set should have 931 // already been freed and we would not succeed in loading the font. 932 MOZ_ASSERT_IF(loaded, mLoadingFontSet); 933 mLoadingFontSet = nullptr; 934 } 935 936 void gfxUserFontEntry::FontLoadFailed(nsIFontLoadCompleteCallback* aCallback) { 937 MOZ_ASSERT(NS_IsMainThread()); 938 939 // Error occurred. Make sure the FontFace's promise is rejected if the 940 // load timed out, or else load the next src. 941 if (mFontDataLoadingState == LOADING_TIMED_OUT) { 942 mFontDataLoadingState = LOADING_FAILED; 943 SetLoadState(STATUS_FAILED); 944 } else { 945 LoadNextSrc(); 946 } 947 948 // We ignore the status returned by LoadNext(); 949 // even if loading failed, we need to bump the font-set generation and return 950 // true in order to trigger reflow, so that fallback will be used where the 951 // text was "masked" by the pending download. 952 aCallback->FontLoadComplete(); 953 } 954 955 void gfxUserFontEntry::GetUserFontSets( 956 nsTArray<RefPtr<gfxUserFontSet>>& aResult) { 957 aResult.Clear(); 958 RefPtr<gfxUserFontSet> fontSet = GetUserFontSet(); 959 if (fontSet) { 960 aResult.AppendElement(std::move(fontSet)); 961 } 962 } 963 964 gfxUserFontSet::gfxUserFontSet() 965 : mFontFamilies(4), 966 mRebuildGeneration(0), 967 mLocalRulesUsed(false), 968 mRebuildLocalRules(false), 969 mDownloadCount(0), 970 mDownloadSize(0), 971 mMutex("gfxUserFontSet") { 972 IncrementGeneration(true); 973 } 974 975 gfxUserFontSet::~gfxUserFontSet() { Destroy(); } 976 977 void gfxUserFontSet::Destroy() { 978 if (auto* pfl = gfxPlatformFontList::PlatformFontList(false)) { 979 pfl->RemoveUserFontSet(this); 980 } 981 982 mFontFamilies.Clear(); 983 } 984 985 already_AddRefed<gfxUserFontEntry> gfxUserFontSet::FindOrCreateUserFontEntry( 986 nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList, 987 gfxUserFontAttributes&& aAttr) { 988 RefPtr<gfxUserFontEntry> entry; 989 990 // If there's already a userfont entry in the family whose descriptors all 991 // match, we can just move it to the end of the list instead of adding a new 992 // face that will always "shadow" the old one. 993 // Note that we can't do this for platform font entries, even if the 994 // style descriptors match, as they might have had a different source list, 995 // but we no longer have the old source list available to check. 996 RefPtr<gfxUserFontFamily> family = LookupFamily(aAttr.mFamilyName); 997 if (family) { 998 entry = FindExistingUserFontEntry(family, aFontFaceSrcList, aAttr); 999 } 1000 1001 if (!entry) { 1002 entry = CreateUserFontEntry(std::move(aFontFaceSrcList), std::move(aAttr)); 1003 } 1004 1005 return entry.forget(); 1006 } 1007 1008 gfxUserFontEntry* gfxUserFontSet::FindExistingUserFontEntry( 1009 gfxUserFontFamily* aFamily, 1010 const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 1011 const gfxUserFontAttributes& aAttr) { 1012 aFamily->ReadLock(); 1013 const auto& fontList = aFamily->GetFontList(); 1014 gfxUserFontEntry* result = nullptr; 1015 1016 for (const auto& font : fontList) { 1017 if (!font->mIsUserFontContainer) { 1018 continue; 1019 } 1020 1021 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(font.get()); 1022 if (ufe->Matches(aFontFaceSrcList, aAttr)) { 1023 result = ufe; 1024 break; 1025 } 1026 } 1027 aFamily->ReadUnlock(); 1028 1029 return result; 1030 } 1031 1032 void gfxUserFontSet::AddUserFontEntry(const nsCString& aFamilyName, 1033 gfxUserFontEntry* aUserFontEntry) { 1034 RefPtr<gfxUserFontFamily> family = GetFamily(aFamilyName); 1035 family->AddFontEntry(aUserFontEntry); 1036 1037 if (LOG_ENABLED()) { 1038 nsAutoCString weightString; 1039 aUserFontEntry->Weight().ToString(weightString); 1040 nsAutoCString stretchString; 1041 aUserFontEntry->Stretch().ToString(stretchString); 1042 LOG( 1043 ("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s " 1044 "stretch: %s display: %d", 1045 this, aFamilyName.get(), aUserFontEntry, 1046 (aUserFontEntry->IsItalic() 1047 ? "italic" 1048 : (aUserFontEntry->IsOblique() ? "oblique" : "normal")), 1049 weightString.get(), stretchString.get(), 1050 static_cast<int>(aUserFontEntry->GetFontDisplay()))); 1051 } 1052 } 1053 1054 void gfxUserFontSet::IncrementGenerationLocked(bool aIsRebuild) { 1055 // add one, increment again if zero 1056 do { 1057 mGeneration = ++sFontSetGeneration; 1058 } while (mGeneration == 0); 1059 if (aIsRebuild) { 1060 mRebuildGeneration = mGeneration; 1061 } 1062 } 1063 1064 void gfxUserFontSet::RebuildLocalRules() { 1065 if (mLocalRulesUsed) { 1066 mRebuildLocalRules = true; 1067 DoRebuildUserFontSet(); 1068 } 1069 } 1070 1071 already_AddRefed<gfxUserFontFamily> gfxUserFontSet::LookupFamily( 1072 const nsACString& aFamilyName) const { 1073 nsAutoCString key(aFamilyName); 1074 ToLowerCase(key); 1075 1076 return mFontFamilies.Get(key); 1077 } 1078 1079 already_AddRefed<gfxUserFontFamily> gfxUserFontSet::GetFamily( 1080 const nsACString& aFamilyName) { 1081 nsAutoCString key(aFamilyName); 1082 ToLowerCase(key); 1083 1084 return do_AddRef(mFontFamilies.GetOrInsertNew(key, aFamilyName)); 1085 } 1086 1087 void gfxUserFontSet::ForgetLocalFaces() { 1088 for (const auto& fam : mFontFamilies.Values()) { 1089 ForgetLocalFace(fam); 1090 } 1091 } 1092 1093 void gfxUserFontSet::ForgetLocalFace(gfxUserFontFamily* aFontFamily) { 1094 // Entries for which we might need to cancel a current loader. 1095 AutoTArray<RefPtr<gfxUserFontEntry>, 8> entriesToCancel; 1096 1097 // Lock the font family while we iterate over its entries. 1098 aFontFamily->ReadLock(); 1099 const auto& fonts = aFontFamily->GetFontList(); 1100 for (const auto& f : fonts) { 1101 auto ufe = static_cast<gfxUserFontEntry*>(f.get()); 1102 // If the user font entry has loaded an entry using src:local(), 1103 // discard it as no longer valid. 1104 if (ufe->GetPlatformFontEntry() && 1105 ufe->GetPlatformFontEntry()->IsLocalUserFont()) { 1106 ufe->mPlatformFontEntry = nullptr; 1107 } 1108 // If the entry had a local source, we need to re-evaluate the source list 1109 // in the context of the new platform fontlist, whether or not the entry 1110 // actually used a local() source last time, as one might have been added. 1111 if (ufe->mSeenLocalSource) { 1112 entriesToCancel.AppendElement(ufe); 1113 } 1114 } 1115 aFontFamily->ReadUnlock(); 1116 1117 // Cancel any current loaders and reset the state of the affected entries. 1118 for (auto& ufe : entriesToCancel) { 1119 if (auto* loader = ufe->GetLoader()) { 1120 // If there's a loader, we need to cancel it, because we'll trigger a 1121 // fresh load if required when we re-resolve the font... 1122 loader->Cancel(); 1123 RemoveLoader(loader); 1124 } else { 1125 // ...otherwise, just reset our state so that we'll re-evaluate the 1126 // source list from the beginning. 1127 ufe->LoadCanceled(); 1128 } 1129 } 1130 } 1131 1132 /////////////////////////////////////////////////////////////////////////////// 1133 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts 1134 // across pages/fontsets rather than instantiating new platform fonts. 1135 // 1136 // Entries are added to this cache when a platform font is instantiated from 1137 // downloaded data, and removed when the platform font entry is destroyed. 1138 // We don't need to use a timed expiration scheme here because the gfxFontEntry 1139 // for a downloaded font will be kept alive by its corresponding gfxFont 1140 // instance(s) until they are deleted, and *that* happens using an expiration 1141 // tracker (gfxFontCache). The result is that the downloaded font instances 1142 // recorded here will persist between pages and can get reused (provided the 1143 // source URI and principal match, of course). 1144 /////////////////////////////////////////////////////////////////////////////// 1145 1146 nsTHashtable<gfxUserFontSet::UserFontCache::Entry>* 1147 gfxUserFontSet::UserFontCache::sUserFonts = nullptr; 1148 1149 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver) 1150 1151 NS_IMETHODIMP 1152 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject, 1153 const char* aTopic, 1154 const char16_t* aData) { 1155 if (!sUserFonts) { 1156 return NS_OK; 1157 } 1158 1159 if (!strcmp(aTopic, "cacheservice:empty-cache")) { 1160 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) { 1161 i.Remove(); 1162 } 1163 } else if (!strcmp(aTopic, "last-pb-context-exited")) { 1164 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) { 1165 if (i.Get()->IsPrivate()) { 1166 i.Remove(); 1167 } 1168 } 1169 } else if (!strcmp(aTopic, "xpcom-shutdown")) { 1170 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) { 1171 i.Get()->GetFontEntry()->DisconnectSVG(); 1172 } 1173 } else { 1174 MOZ_ASSERT_UNREACHABLE("unexpected topic"); 1175 } 1176 1177 return NS_OK; 1178 } 1179 1180 bool gfxUserFontSet::UserFontCache::Entry::KeyEquals( 1181 const KeyTypePointer aKey) const { 1182 const gfxFontEntry* fe = aKey->mFontEntry; 1183 1184 if (!mURI->Equals(aKey->mURI)) { 1185 return false; 1186 } 1187 1188 // For data: URIs, we don't care about the principal; otherwise, check it. 1189 if (!IgnorePrincipal(mURI)) { 1190 NS_ASSERTION(mPrincipal && aKey->mPrincipal, 1191 "only data: URIs are allowed to omit the principal"); 1192 if (!mPrincipal->Equals(aKey->mPrincipal)) { 1193 return false; 1194 } 1195 } 1196 1197 if (mPrivate != aKey->mPrivate) { 1198 return false; 1199 } 1200 1201 if (mFontEntry->SlantStyle() != fe->SlantStyle() || 1202 mFontEntry->Weight() != fe->Weight() || 1203 mFontEntry->Stretch() != fe->Stretch() || 1204 mFontEntry->AutoRangeFlags() != fe->AutoRangeFlags() || 1205 mFontEntry->mFeatureSettings != fe->mFeatureSettings || 1206 mFontEntry->mVariationSettings != fe->mVariationSettings || 1207 mFontEntry->mLanguageOverride != fe->mLanguageOverride || 1208 mFontEntry->mAscentOverride != fe->mAscentOverride || 1209 mFontEntry->mDescentOverride != fe->mDescentOverride || 1210 mFontEntry->mLineGapOverride != fe->mLineGapOverride || 1211 mFontEntry->mSizeAdjust != fe->mSizeAdjust || 1212 mFontEntry->mFamilyName != fe->mFamilyName) { 1213 return false; 1214 } 1215 1216 return true; 1217 } 1218 1219 void gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry) { 1220 // if caching is disabled, simply return 1221 if (StaticPrefs::gfx_downloadable_fonts_disable_cache()) { 1222 return; 1223 } 1224 1225 gfxUserFontData* data = aFontEntry->mUserFontData.get(); 1226 if (data->mIsBuffer) { 1227 #ifdef DEBUG_USERFONT_CACHE 1228 printf("userfontcache skipped fontentry with buffer source: %p\n", 1229 aFontEntry); 1230 #endif 1231 return; 1232 } 1233 1234 if (!sUserFonts) { 1235 sUserFonts = new nsTHashtable<Entry>; 1236 1237 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 1238 if (obs) { 1239 Flusher* flusher = new Flusher; 1240 obs->AddObserver(flusher, "cacheservice:empty-cache", false); 1241 obs->AddObserver(flusher, "last-pb-context-exited", false); 1242 obs->AddObserver(flusher, "xpcom-shutdown", false); 1243 } 1244 1245 // Create and register a memory reporter for sUserFonts. 1246 // This reporter is never unregistered, but that's OK because 1247 // the reporter checks whether sUserFonts is null, so it would 1248 // be safe to call even after UserFontCache::Shutdown has deleted 1249 // the cache. 1250 RegisterStrongMemoryReporter(new MemoryReporter()); 1251 } 1252 1253 // For data: URIs, the principal is ignored; anyone who has the same 1254 // data: URI is able to load it and get an equivalent font. 1255 // Otherwise, the principal is used as part of the cache key. 1256 gfxFontSrcPrincipal* principal; 1257 if (IgnorePrincipal(data->mURI)) { 1258 principal = nullptr; 1259 } else { 1260 principal = data->mPrincipal; 1261 } 1262 sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry, data->mPrivate)); 1263 1264 #ifdef DEBUG_USERFONT_CACHE 1265 printf("userfontcache added fontentry: %p\n", aFontEntry); 1266 Dump(); 1267 #endif 1268 } 1269 1270 void gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry) { 1271 if (!sUserFonts) { 1272 // if we've already deleted the cache (i.e. during shutdown), 1273 // just ignore this 1274 return; 1275 } 1276 1277 // We can't simply use RemoveEntry here because it's possible the principal 1278 // may have changed since the font was cached, in which case the lookup 1279 // would no longer find the entry (bug 838105). 1280 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) { 1281 if (i.Get()->GetFontEntry() == aFontEntry) { 1282 i.Remove(); 1283 } 1284 } 1285 1286 #ifdef DEBUG_USERFONT_CACHE 1287 printf("userfontcache removed fontentry: %p\n", aFontEntry); 1288 Dump(); 1289 #endif 1290 } 1291 1292 gfxFontEntry* gfxUserFontSet::UserFontCache::GetFont( 1293 const gfxFontFaceSrc& aSrc, const gfxUserFontEntry& aUserFontEntry) { 1294 if (!sUserFonts || StaticPrefs::gfx_downloadable_fonts_disable_cache()) { 1295 return nullptr; 1296 } 1297 1298 RefPtr<gfxUserFontSet> srcFontSet = aUserFontEntry.GetUserFontSet(); 1299 if (NS_WARN_IF(!srcFontSet) || srcFontSet->BypassCache()) { 1300 return nullptr; 1301 } 1302 1303 // Ignore principal when looking up a data: URI. 1304 RefPtr<gfxFontSrcPrincipal> principal = 1305 IgnorePrincipal(aSrc.mURI) ? nullptr : aSrc.LoadPrincipal(*srcFontSet); 1306 1307 Entry* entry = sUserFonts->GetEntry( 1308 Key(aSrc.mURI, principal, const_cast<gfxUserFontEntry*>(&aUserFontEntry), 1309 srcFontSet->GetPrivateBrowsing())); 1310 if (!entry) { 1311 return nullptr; 1312 } 1313 1314 // We have to perform another content policy check here to prevent 1315 // cache poisoning. E.g. a.com loads a font into the cache but 1316 // b.com has a CSP not allowing any fonts to be loaded. 1317 if (!srcFontSet->IsFontLoadAllowed(aSrc)) { 1318 return nullptr; 1319 } 1320 1321 return entry->GetFontEntry(); 1322 } 1323 1324 void gfxUserFontSet::UserFontCache::Shutdown() { 1325 if (sUserFonts) { 1326 delete sUserFonts; 1327 sUserFonts = nullptr; 1328 } 1329 } 1330 1331 MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf) 1332 1333 void gfxUserFontSet::UserFontCache::Entry::ReportMemory( 1334 nsIHandleReportCallback* aHandleReport, nsISupports* aData, 1335 bool aAnonymize) { 1336 MOZ_ASSERT(mFontEntry); 1337 nsAutoCString path("explicit/gfx/user-fonts/font("); 1338 1339 if (aAnonymize) { 1340 path.AppendPrintf("<anonymized-%p>", this); 1341 } else { 1342 path.AppendPrintf("family=%s", mFontEntry->mFamilyName.get()); 1343 if (mURI) { 1344 nsCString spec = mURI->GetSpecOrDefault(); 1345 spec.ReplaceChar('/', '\\'); 1346 // Some fonts are loaded using horrendously-long data: URIs; 1347 // truncate those before reporting them. 1348 if (mURI->get()->SchemeIs("data") && spec.Length() > 255) { 1349 spec.Truncate(252); 1350 spec.AppendLiteral("..."); 1351 } 1352 path.AppendPrintf(", url=%s", spec.get()); 1353 } 1354 if (mPrincipal) { 1355 nsAutoCString spec; 1356 mPrincipal->NodePrincipal()->GetAsciiSpec(spec); 1357 if (!spec.IsEmpty()) { 1358 // Include a clue as to who loaded this resource. (Note 1359 // that because of font entry sharing, other pages may now 1360 // be using this resource, and the original page may not 1361 // even be loaded any longer.) 1362 spec.ReplaceChar('/', '\\'); 1363 path.AppendPrintf(", principal=%s", spec.get()); 1364 } 1365 } 1366 } 1367 path.Append(')'); 1368 1369 aHandleReport->Callback( 1370 ""_ns, path, nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, 1371 mFontEntry->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf), 1372 "Memory used by @font-face resource."_ns, aData); 1373 } 1374 1375 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter, 1376 nsIMemoryReporter) 1377 1378 NS_IMETHODIMP 1379 gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports( 1380 nsIHandleReportCallback* aHandleReport, nsISupports* aData, 1381 bool aAnonymize) { 1382 if (!sUserFonts) { 1383 return NS_OK; 1384 } 1385 1386 for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) { 1387 it.Get()->ReportMemory(aHandleReport, aData, aAnonymize); 1388 } 1389 1390 MOZ_COLLECT_REPORT( 1391 "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP, UNITS_BYTES, 1392 sUserFonts->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf), 1393 "Memory used by the @font-face cache, not counting the actual font " 1394 "resources."); 1395 1396 return NS_OK; 1397 } 1398 1399 #ifdef DEBUG_USERFONT_CACHE 1400 1401 void gfxUserFontSet::UserFontCache::Entry::Dump() { 1402 nsresult rv; 1403 1404 nsAutoCString principalURISpec("(null)"); 1405 bool setDomain = false; 1406 1407 if (mPrincipal) { 1408 nsCOMPtr<nsIURI> principalURI; 1409 rv = mPrincipal->NodePrincipal()->GetURI(getter_AddRefs(principalURI)); 1410 if (NS_SUCCEEDED(rv)) { 1411 principalURI->GetSpec(principalURISpec); 1412 } 1413 1414 nsCOMPtr<nsIURI> domainURI; 1415 mPrincipal->NodePrincipal()->GetDomain(getter_AddRefs(domainURI)); 1416 if (domainURI) { 1417 setDomain = true; 1418 } 1419 } 1420 1421 NS_ASSERTION(mURI, "null URI in userfont cache entry"); 1422 1423 printf( 1424 "userfontcache fontEntry: %p fonturihash: %8.8x " 1425 "family: %s domainset: %s principal: [%s]\n", 1426 mFontEntry, mURI->Hash(), mFontEntry->FamilyName().get(), 1427 setDomain ? "true" : "false", principalURISpec.get()); 1428 } 1429 1430 void gfxUserFontSet::UserFontCache::Dump() { 1431 if (!sUserFonts) { 1432 return; 1433 } 1434 1435 printf("userfontcache dump count: %d ========\n", sUserFonts->Count()); 1436 for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) { 1437 it.Get()->Dump(); 1438 } 1439 printf("userfontcache dump ==================\n"); 1440 } 1441 1442 #endif 1443 1444 #undef LOG 1445 #undef LOG_ENABLED