FontFaceSetDocumentImpl.cpp (25917B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FontFaceSetDocumentImpl.h" 8 9 #include "mozilla/FontLoaderUtils.h" 10 #include "mozilla/LoadInfo.h" 11 #include "mozilla/PresShell.h" 12 #include "mozilla/PresShellInlines.h" 13 #include "mozilla/css/Loader.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/DocumentInlines.h" 16 #include "mozilla/dom/Event.h" 17 #include "mozilla/dom/FontFaceImpl.h" 18 #include "mozilla/dom/FontFaceSet.h" 19 #include "nsContentPolicyUtils.h" 20 #include "nsDOMNavigationTiming.h" 21 #include "nsFontFaceLoader.h" 22 #include "nsIDocShell.h" 23 #include "nsISupportsPriority.h" 24 #include "nsIWebNavigation.h" 25 #include "nsPresContext.h" 26 27 using namespace mozilla; 28 using namespace mozilla::css; 29 using namespace mozilla::dom; 30 31 #define LOG(args) \ 32 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args) 33 #define LOG_ENABLED() \ 34 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug) 35 36 NS_IMPL_ISUPPORTS_INHERITED(FontFaceSetDocumentImpl, FontFaceSetImpl, 37 nsIDOMEventListener, nsICSSLoaderObserver) 38 39 FontFaceSetDocumentImpl::FontFaceSetDocumentImpl(FontFaceSet* aOwner, 40 dom::Document* aDocument) 41 : FontFaceSetImpl(aOwner), mDocument(aDocument) {} 42 43 FontFaceSetDocumentImpl::~FontFaceSetDocumentImpl() = default; 44 45 void FontFaceSetDocumentImpl::Initialize() { 46 RecursiveMutexAutoLock lock(mMutex); 47 48 MOZ_ASSERT(mDocument, "We should get a valid document from the caller!"); 49 50 // Record the state of the "bypass cache" flags from the docshell now, 51 // since we want to look at them from style worker threads, and we can 52 // only get to the docshell through a weak pointer (which is only 53 // possible on the main thread). 54 // 55 // In theory the load type of a docshell could change after the document 56 // is loaded, but handling that doesn't seem too important. 57 if (nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell()) { 58 uint32_t loadType; 59 uint32_t flags; 60 if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) && 61 ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) || 62 (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) && 63 (flags & nsIRequest::LOAD_BYPASS_CACHE))) { 64 mBypassCache = true; 65 } 66 } 67 68 // Same for the "private browsing" flag. 69 if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) { 70 mPrivateBrowsing = loadContext->UsePrivateBrowsing(); 71 } 72 73 if (!mDocument->DidFireDOMContentLoaded()) { 74 mDocument->AddSystemEventListener(u"DOMContentLoaded"_ns, this, false, 75 false); 76 } else { 77 // In some cases we can't rely on CheckLoadingFinished being called from 78 // the refresh driver. For example, documents in display:none iframes. 79 // Or if the document has finished loading and painting at the time that 80 // script requests document.fonts and causes us to get here. 81 CheckLoadingFinished(); 82 } 83 84 mDocument->EnsureCSSLoader().AddObserver(this); 85 86 mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>( 87 mDocument->NodePrincipal(), mDocument->PartitionedPrincipal()); 88 } 89 90 void FontFaceSetDocumentImpl::Destroy() { 91 RemoveDOMContentLoadedListener(); 92 93 if (mDocument && mDocument->GetExistingCSSLoader()) { 94 // We're null checking CSSLoader() since FontFaceSetImpl::Disconnect() might 95 // be being called during unlink, at which time the loader may already have 96 // been unlinked from the document. 97 mDocument->GetExistingCSSLoader()->RemoveObserver(this); 98 } 99 100 mRuleFaces.Clear(); 101 102 // Since the base class may depend on the document remaining set, we need to 103 // clear mDocument after. 104 FontFaceSetImpl::Destroy(); 105 106 mDocument = nullptr; 107 } 108 109 bool FontFaceSetDocumentImpl::IsOnOwningThread() { return NS_IsMainThread(); } 110 111 #ifdef DEBUG 112 void FontFaceSetDocumentImpl::AssertIsOnOwningThread() { 113 MOZ_ASSERT(NS_IsMainThread()); 114 } 115 #endif 116 117 void FontFaceSetDocumentImpl::DispatchToOwningThread( 118 const char* aName, std::function<void()>&& aFunc) { 119 class FontFaceSetDocumentRunnable final : public Runnable { 120 public: 121 FontFaceSetDocumentRunnable(const char* aName, 122 std::function<void()>&& aFunc) 123 : Runnable(aName), mFunc(std::move(aFunc)) {} 124 125 NS_IMETHOD Run() final { 126 mFunc(); 127 return NS_OK; 128 } 129 130 private: 131 std::function<void()> mFunc; 132 }; 133 134 auto runnable = 135 MakeRefPtr<FontFaceSetDocumentRunnable>(aName, std::move(aFunc)); 136 NS_DispatchToMainThread(runnable.forget()); 137 } 138 139 uint64_t FontFaceSetDocumentImpl::GetInnerWindowID() { 140 MOZ_ASSERT(NS_IsMainThread()); 141 if (!mDocument) { 142 return 0; 143 } 144 145 return mDocument->InnerWindowID(); 146 } 147 148 FontVisibilityProvider* FontFaceSetDocumentImpl::GetFontVisibilityProvider() 149 const { 150 mozilla::AssertIsMainThreadOrServoFontMetricsLocked(); 151 if (!mDocument) { 152 return nullptr; 153 } 154 155 return mDocument->GetPresContext(); 156 } 157 158 void FontFaceSetDocumentImpl::RefreshStandardFontLoadPrincipal() { 159 MOZ_ASSERT(NS_IsMainThread()); 160 RecursiveMutexAutoLock lock(mMutex); 161 if (NS_WARN_IF(!mDocument)) { 162 return; 163 } 164 mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>( 165 mDocument->NodePrincipal(), mDocument->PartitionedPrincipal()); 166 FontFaceSetImpl::RefreshStandardFontLoadPrincipal(); 167 } 168 169 already_AddRefed<URLExtraData> FontFaceSetDocumentImpl::GetURLExtraData() { 170 if (!mDocument) { 171 return nullptr; 172 } 173 return do_AddRef(mDocument->DefaultStyleAttrURLData()); 174 } 175 176 void FontFaceSetDocumentImpl::RemoveDOMContentLoadedListener() { 177 if (mDocument) { 178 mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false); 179 } 180 } 181 182 void FontFaceSetDocumentImpl::FindMatchingFontFaces( 183 const nsTHashSet<FontFace*>& aMatchingFaces, 184 nsTArray<FontFace*>& aFontFaces) { 185 FontFaceSetImpl::FindMatchingFontFaces(aMatchingFaces, aFontFaces); 186 for (FontFaceRecord& record : mRuleFaces) { 187 FontFace* owner = record.mFontFace->GetOwner(); 188 if (owner && aMatchingFaces.Contains(owner)) { 189 aFontFaces.AppendElement(owner); 190 } 191 } 192 } 193 194 TimeStamp FontFaceSetDocumentImpl::GetNavigationStartTimeStamp() { 195 TimeStamp navStart; 196 RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming()); 197 if (timing) { 198 navStart = timing->GetNavigationStartTimeStamp(); 199 } 200 return navStart; 201 } 202 203 void FontFaceSetDocumentImpl::EnsureReady() { 204 MOZ_ASSERT(NS_IsMainThread()); 205 206 // There may be outstanding style changes that will trigger the loading of 207 // new fonts. We need to flush layout to initiate any such loads so that 208 // if mReady is currently resolved we replace it with a new pending Promise. 209 // (That replacement will happen under this flush call.) 210 if (!ReadyPromiseIsPending() && mDocument) { 211 mDocument->FlushPendingNotifications(FlushType::Layout); 212 } 213 } 214 215 #ifdef DEBUG 216 bool FontFaceSetDocumentImpl::HasRuleFontFace(FontFaceImpl* aFontFace) { 217 for (const auto& record : mRuleFaces) { 218 if (record.mFontFace == aFontFace) { 219 return true; 220 } 221 } 222 return false; 223 } 224 #endif 225 226 bool FontFaceSetDocumentImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) { 227 if (NS_WARN_IF(!mDocument)) { 228 return false; 229 } 230 231 if (!FontFaceSetImpl::Add(aFontFace, aRv)) { 232 return false; 233 } 234 235 RefPtr<dom::Document> clonedDoc = mDocument->GetLatestStaticClone(); 236 if (clonedDoc) { 237 // The document is printing, copy the font to the static clone as well. 238 nsCOMPtr<nsIPrincipal> principal = mDocument->GetPrincipal(); 239 if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) { 240 ErrorResult rv; 241 clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv); 242 MOZ_ASSERT(!rv.Failed()); 243 } 244 } 245 246 return true; 247 } 248 249 nsresult FontFaceSetDocumentImpl::StartLoad(gfxUserFontEntry* aUserFontEntry, 250 uint32_t aSrcIndex) { 251 if (NS_WARN_IF(!mDocument)) { 252 return NS_ERROR_FAILURE; 253 } 254 255 nsresult rv; 256 257 nsCOMPtr<nsIStreamLoader> streamLoader; 258 RefPtr<nsFontFaceLoader> fontLoader; 259 260 const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex); 261 262 auto preloadKey = 263 PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS); 264 RefPtr<PreloaderBase> preload = 265 mDocument->Preloads().LookupPreload(preloadKey); 266 267 if (preload) { 268 fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, 269 preload->Channel()); 270 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, 271 fontLoader); 272 NS_ENSURE_SUCCESS(rv, rv); 273 274 rv = preload->AsyncConsume(streamLoader); 275 276 // We don't want this to hang around regardless of the result, there will be 277 // no coalescing of later found <link preload> tags for fonts. 278 preload->RemoveSelf(mDocument); 279 } else { 280 // No preload found, open a channel. 281 rv = NS_ERROR_FAILURE; 282 } 283 284 nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup()); 285 if (NS_FAILED(rv)) { 286 nsCOMPtr<nsIChannel> channel; 287 rv = FontLoaderUtils::BuildChannel( 288 getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS, 289 dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src, 290 mDocument, loadGroup, nullptr, false, 291 nsISupportsPriority::PRIORITY_HIGH); 292 NS_ENSURE_SUCCESS(rv, rv); 293 294 fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel); 295 296 if (LOG_ENABLED()) { 297 nsCOMPtr<nsIURI> referrer = src.mReferrerInfo 298 ? src.mReferrerInfo->GetOriginalReferrer() 299 : nullptr; 300 LOG(( 301 "userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n", 302 fontLoader.get(), src.mURI->GetSpecOrDefault().get(), 303 referrer ? referrer->GetSpecOrDefault().get() : "")); 304 } 305 306 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, 307 fontLoader); 308 NS_ENSURE_SUCCESS(rv, rv); 309 310 rv = channel->AsyncOpen(streamLoader); 311 if (NS_FAILED(rv)) { 312 fontLoader->DropChannel(); // explicitly need to break ref cycle 313 } 314 } 315 316 { 317 RecursiveMutexAutoLock lock(mMutex); 318 mLoaders.PutEntry(fontLoader); 319 } 320 321 if (NS_SUCCEEDED(rv)) { 322 fontLoader->StartedLoading(streamLoader); 323 // let the font entry remember the loader, in case we need to cancel it 324 aUserFontEntry->SetLoader(fontLoader); 325 } 326 327 return rv; 328 } 329 330 bool FontFaceSetDocumentImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) { 331 MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL); 332 333 if (ServoStyleSet::IsInServoTraversal()) { 334 RecursiveMutexAutoLock lock(mMutex); 335 auto entry = mAllowedFontLoads.Lookup(&aSrc); 336 MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?"); 337 return entry ? *entry : false; 338 } 339 340 MOZ_ASSERT(NS_IsMainThread()); 341 342 if (aSrc.mUseOriginPrincipal) { 343 return true; 344 } 345 346 if (NS_WARN_IF(!mDocument)) { 347 return false; 348 } 349 350 RefPtr<gfxFontSrcPrincipal> gfxPrincipal = 351 aSrc.mURI->InheritsSecurityContext() ? nullptr 352 : aSrc.LoadPrincipal(*this); 353 354 nsIPrincipal* principal = 355 gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr; 356 357 Result<RefPtr<net::LoadInfo>, nsresult> maybeLoadInfo = net::LoadInfo::Create( 358 mDocument->NodePrincipal(), // loading principal 359 principal, // triggering principal 360 mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 361 nsIContentPolicy::TYPE_FONT); 362 if (NS_WARN_IF(maybeLoadInfo.isErr())) { 363 return false; 364 } 365 RefPtr<net::LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap(); 366 367 int16_t shouldLoad = nsIContentPolicy::ACCEPT; 368 nsresult rv = 369 NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &shouldLoad, 370 nsContentUtils::GetContentPolicy()); 371 372 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); 373 } 374 375 nsresult FontFaceSetDocumentImpl::CreateChannelForSyncLoadFontData( 376 nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, 377 const gfxFontFaceSrc* aFontFaceSrc) { 378 gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal(); 379 380 // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a 381 // node and a principal. This is because the document where the font is 382 // being loaded might have a different origin from the principal of the 383 // stylesheet that initiated the font load. 384 // Further, we only get here for data: loads, so it doesn't really matter 385 // whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be 386 // more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT. 387 return NS_NewChannelWithTriggeringPrincipal( 388 aOutChannel, aFontFaceSrc->mURI->get(), mDocument, 389 principal ? principal->NodePrincipal() : nullptr, 390 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, 391 aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT 392 : nsIContentPolicy::TYPE_FONT); 393 } 394 395 bool FontFaceSetDocumentImpl::UpdateRules( 396 const nsTArray<nsFontFaceRuleContainer>& aRules) { 397 RecursiveMutexAutoLock lock(mMutex); 398 399 // If there was a change to the mNonRuleFaces array, then there could 400 // have been a modification to the user font set. 401 bool modified = mNonRuleFacesDirty; 402 mNonRuleFacesDirty = false; 403 404 // The @font-face rules that make up the user font set have changed, 405 // so we need to update the set. However, we want to preserve existing 406 // font entries wherever possible, so that we don't discard and then 407 // re-download resources in the (common) case where at least some of the 408 // same rules are still present. 409 nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces); 410 411 // We reverse the oldRecords array because we will most likely be using the 412 // entries in the order they were originally added, and constantly removing 413 // the first element is inefficient if the array is large; it's better if 414 // we're most often removing elements from the end. 415 oldRecords.Reverse(); 416 417 // Remove faces from the font family records; we need to re-insert them 418 // because we might end up with faces in a different order even if they're 419 // the same font entries as before. (The order can affect font selection 420 // where multiple faces match the requested style, perhaps with overlapping 421 // unicode-range coverage.) 422 for (const auto& fontFamily : mFontFamilies.Values()) { 423 fontFamily->DetachFontEntries(); 424 } 425 426 // Sometimes aRules has duplicate @font-face rules in it; we should make 427 // that not happen, but in the meantime, don't try to insert the same 428 // FontFace object more than once into mRuleFaces. We track which 429 // ones we've handled in this table. 430 nsTHashSet<StyleLockedFontFaceRule*> handledRules; 431 432 for (const nsFontFaceRuleContainer& container : aRules) { 433 // Insert each FontFace objects for each rule into our list, migrating old 434 // font entries if possible rather than creating new ones; set modified to 435 // true if we detect that rule ordering has changed, or if a new entry is 436 // created. 437 StyleLockedFontFaceRule* rule = container.mRule; 438 if (!handledRules.EnsureInserted(rule)) { 439 // rule was already present in the hashtable 440 continue; 441 } 442 modified |= InsertRuleFontFace(rule, container.mOrigin, oldRecords); 443 } 444 445 for (const FontFaceRecord& record : mNonRuleFaces) { 446 // Do the same for the non rule backed FontFace objects. 447 InsertNonRuleFontFace(record.mFontFace); 448 } 449 450 // Remove any residual families that have no font entries (i.e., they were 451 // not defined at all by the updated set of @font-face rules). 452 for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) { 453 if (!it.Data()->FontListLength()) { 454 it.Remove(); 455 } 456 } 457 458 // If any FontFace objects for rules are left in the old list, note that the 459 // set has changed (even if the new set was built entirely by migrating old 460 // font entries). 461 if (!oldRecords.IsEmpty()) { 462 modified = true; 463 // Any in-progress loaders for obsolete rules should be cancelled, 464 // as the resource being downloaded will no longer be required. 465 // We need to explicitly remove any loaders here, otherwise the loaders 466 // will keep their "orphaned" font entries alive until they complete, 467 // even after the oldRules array is deleted. 468 // 469 // XXX Now that it is possible for the author to hold on to a rule backed 470 // FontFace object, we shouldn't cancel loading here; instead we should do 471 // it when the FontFace is GCed, if we can detect that. 472 for (const FontFaceRecord& record : oldRecords) { 473 RefPtr<FontFaceImpl> f = record.mFontFace; 474 if (gfxUserFontEntry* userFontEntry = f->GetUserFontEntry()) { 475 if (nsFontFaceLoader* loader = userFontEntry->GetLoader()) { 476 loader->Cancel(); 477 RemoveLoader(loader); 478 } 479 } 480 481 // Any left over FontFace objects should also cease being rule backed. 482 f->DisconnectFromRule(); 483 } 484 } 485 486 if (modified) { 487 IncrementGenerationLocked(true); 488 mHasLoadingFontFacesIsDirty = true; 489 CheckLoadingStarted(); 490 CheckLoadingFinished(); 491 } 492 493 // if local rules needed to be rebuilt, they have been rebuilt at this point 494 if (mRebuildLocalRules) { 495 mLocalRulesUsed = false; 496 mRebuildLocalRules = false; 497 } 498 499 if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) { 500 LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this, 501 (modified ? "modified" : "not modified"), (int)(mRuleFaces.Length()))); 502 } 503 504 return modified; 505 } 506 507 bool FontFaceSetDocumentImpl::InsertRuleFontFace( 508 StyleLockedFontFaceRule* aRule, StyleOrigin aSheetType, 509 nsTArray<FontFaceRecord>& aOldRecords) { 510 RecursiveMutexAutoLock lock(mMutex); 511 512 if (MOZ_UNLIKELY(!mOwner)) { 513 return false; 514 } 515 516 gfxUserFontAttributes attr; 517 if (!FontFaceImpl::GetAttributesFromRule(aRule, attr)) { 518 // If there is no family name, this rule cannot contribute a 519 // usable font, so there is no point in processing it further. 520 return false; 521 } 522 523 bool remove = false; 524 size_t removeIndex; 525 526 // This is a rule backed FontFace. First, we check in aOldRecords; if 527 // the FontFace for the rule exists there, just move it to the new record 528 // list, and put the entry into the appropriate family. 529 // Note that aOldRecords was reversed, so we search it from the end. 530 for (size_t i = aOldRecords.Length(); i > 0;) { 531 FontFaceRecord& rec = aOldRecords[--i]; 532 533 const bool matches = 534 rec.mOrigin == Some(aSheetType) && 535 Servo_FontFaceRule_Equals(rec.mFontFace->GetData(), aRule); 536 if (!matches) { 537 continue; 538 } 539 540 FontFace* owner = rec.mFontFace->GetOwner(); 541 // if local rules were used, don't use the old font entry 542 // for rules containing src local usage 543 if (mLocalRulesUsed && mRebuildLocalRules) { 544 const bool hasLocalSource = [&] { 545 for (auto& source : attr.mSources) { 546 if (source.IsLocal()) { 547 return true; 548 } 549 } 550 return false; 551 }(); 552 553 if (hasLocalSource) { 554 // Remove the old record, but wait to see if we successfully create a 555 // new user font entry below. 556 remove = true; 557 removeIndex = i; 558 break; 559 } 560 } 561 562 rec.mFontFace->SetRule(aRule); 563 gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry(); 564 MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now"); 565 566 AddUserFontEntry(attr.mFamilyName, entry); 567 568 MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace), 569 "FontFace should not occur in mRuleFaces twice"); 570 571 mRuleFaces.AppendElement(rec); 572 aOldRecords.RemoveElementAt(i); 573 574 if (owner) { 575 mOwner->InsertRuleFontFace(owner, aSheetType); 576 } 577 578 // Return that the set has been modified if an old rule was skipped to find 579 // this one: something has been dropped, or ordering changed. 580 // Note that the record at index i has been removed, so Length() is now the 581 // original last-element index. 582 return i < aOldRecords.Length(); 583 } 584 585 RefPtr<FontFace> fontFace = 586 FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, aRule); 587 RefPtr<FontFaceImpl> impl = fontFace->GetImpl(); 588 // this is a new rule: 589 nsAutoCString family(attr.mFamilyName); 590 RefPtr<gfxUserFontEntry> entry = 591 FindOrCreateUserFontEntryFromFontFace(impl, std::move(attr), aSheetType); 592 593 if (!entry) { 594 return false; 595 } 596 597 if (remove) { 598 // Although we broke out of the aOldRecords loop above, since we found 599 // src local usage, and we're not using the old user font entry, we still 600 // are adding a record to mRuleFaces with the same FontFace object. 601 // Remove the old record so that we don't have the same FontFace listed 602 // in both mRuleFaces and oldRecords, which would cause us to call 603 // DisconnectFromRule on a FontFace that should still be rule backed. 604 aOldRecords.RemoveElementAt(removeIndex); 605 } 606 607 FontFaceRecord rec; 608 rec.mFontFace = impl; 609 rec.mOrigin = Some(aSheetType); 610 611 impl->SetUserFontEntry(entry); 612 613 MOZ_ASSERT(!HasRuleFontFace(impl), 614 "FontFace should not occur in mRuleFaces twice"); 615 616 mRuleFaces.AppendElement(rec); 617 618 mOwner->InsertRuleFontFace(fontFace, aSheetType); 619 620 // Add the entry to the end of the list. If an existing userfont entry was 621 // returned by FindOrCreateUserFontEntryFromFontFace that was already stored 622 // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry 623 // calls, will automatically remove the earlier occurrence of the same 624 // userfont entry. 625 AddUserFontEntry(family, entry); 626 return true; 627 } 628 629 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForEntry( 630 gfxFontEntry* aFontEntry) { 631 NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries"); 632 for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) { 633 FontFaceImpl* f = mRuleFaces[i].mFontFace; 634 gfxUserFontEntry* entry = f->GetUserFontEntry(); 635 if (entry && entry->GetPlatformFontEntry() == aFontEntry) { 636 return f->GetRule(); 637 } 638 } 639 return nullptr; 640 } 641 642 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForUserFontEntry( 643 gfxUserFontEntry* aUserFontEntry) { 644 for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) { 645 FontFaceImpl* f = mRuleFaces[i].mFontFace; 646 if (f->GetUserFontEntry() == aUserFontEntry) { 647 return f->GetRule(); 648 } 649 } 650 return nullptr; 651 } 652 653 void FontFaceSetDocumentImpl::CacheFontLoadability() { 654 RecursiveMutexAutoLock lock(mMutex); 655 656 // TODO(emilio): We could do it a bit more incrementally maybe? 657 for (const auto& fontFamily : mFontFamilies.Values()) { 658 fontFamily->ReadLock(); 659 for (const gfxFontEntry* entry : fontFamily->GetFontList()) { 660 if (!entry->mIsUserFontContainer) { 661 continue; 662 } 663 664 const auto& sourceList = 665 static_cast<const gfxUserFontEntry*>(entry)->SourceList(); 666 for (const gfxFontFaceSrc& src : sourceList) { 667 if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) { 668 continue; 669 } 670 mAllowedFontLoads.LookupOrInsertWith( 671 &src, [&] { return IsFontLoadAllowed(src); }); 672 } 673 } 674 fontFamily->ReadUnlock(); 675 } 676 } 677 678 void FontFaceSetDocumentImpl::DidRefresh() { CheckLoadingFinished(); } 679 680 void FontFaceSetDocumentImpl::UpdateHasLoadingFontFaces() { 681 RecursiveMutexAutoLock lock(mMutex); 682 FontFaceSetImpl::UpdateHasLoadingFontFaces(); 683 684 if (mHasLoadingFontFaces) { 685 return; 686 } 687 688 for (size_t i = 0; i < mRuleFaces.Length(); i++) { 689 FontFaceImpl* f = mRuleFaces[i].mFontFace; 690 if (f->Status() == FontFaceLoadStatus::Loading) { 691 mHasLoadingFontFaces = true; 692 return; 693 } 694 } 695 } 696 697 bool FontFaceSetDocumentImpl::MightHavePendingFontLoads() { 698 if (FontFaceSetImpl::MightHavePendingFontLoads()) { 699 return true; 700 } 701 702 if (!mDocument) { 703 return false; 704 } 705 706 // Check for pending restyles or reflows, as they might cause fonts to 707 // load as new styles apply and text runs are rebuilt. 708 PresShell* ps = mDocument->GetPresShell(); 709 if (ps && ps->MightHavePendingFontLoads()) { 710 return true; 711 } 712 713 // We defer resolving mReady until the document as fully loaded. 714 if (!mDocument->DidFireDOMContentLoaded()) { 715 return true; 716 } 717 718 // And we also wait for any CSS style sheets to finish loading, as their 719 // styles might cause new fonts to load. 720 if (css::Loader* loader = mDocument->GetExistingCSSLoader()) { 721 if (loader->HasPendingLoads()) { 722 return true; 723 } 724 } 725 726 return false; 727 } 728 729 // nsIDOMEventListener 730 731 NS_IMETHODIMP 732 FontFaceSetDocumentImpl::HandleEvent(Event* aEvent) { 733 nsString type; 734 aEvent->GetType(type); 735 736 if (!type.EqualsLiteral("DOMContentLoaded")) { 737 return NS_ERROR_FAILURE; 738 } 739 740 RemoveDOMContentLoadedListener(); 741 CheckLoadingFinished(); 742 743 return NS_OK; 744 } 745 746 // nsICSSLoaderObserver 747 748 NS_IMETHODIMP 749 FontFaceSetDocumentImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, 750 nsresult aStatus) { 751 CheckLoadingFinished(); 752 return NS_OK; 753 } 754 755 void FontFaceSetDocumentImpl::FlushUserFontSet() { 756 if (mDocument) { 757 mDocument->FlushUserFontSet(); 758 } 759 } 760 761 void FontFaceSetDocumentImpl::MarkUserFontSetDirty() { 762 if (mDocument) { 763 // Ensure we trigger at least a style flush, that will eventually flush the 764 // user font set. Otherwise the font loads that that flush may cause could 765 // never be triggered. 766 if (PresShell* presShell = mDocument->GetPresShell()) { 767 presShell->EnsureStyleFlush(); 768 } 769 mDocument->MarkUserFontSetDirty(); 770 } 771 } 772 773 #undef LOG_ENABLED 774 #undef LOG