Bug 1822393 - consistently use getGeckoViewDependency() in browser-engine-gecko....
[gecko.git] / layout / style / FontFaceSetDocumentImpl.cpp
blob33f7404c7c66969f77113abce572f8fd3ebeff95
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/. */
7 #include "FontFaceSetDocumentImpl.h"
8 #include "mozilla/FontLoaderUtils.h"
9 #include "mozilla/LoadInfo.h"
10 #include "mozilla/PresShell.h"
11 #include "mozilla/PresShellInlines.h"
12 #include "mozilla/css/Loader.h"
13 #include "mozilla/dom/Document.h"
14 #include "mozilla/dom/DocumentInlines.h"
15 #include "mozilla/dom/Event.h"
16 #include "mozilla/dom/FontFaceImpl.h"
17 #include "mozilla/dom/FontFaceSet.h"
18 #include "nsContentPolicyUtils.h"
19 #include "nsDOMNavigationTiming.h"
20 #include "nsFontFaceLoader.h"
21 #include "nsIDocShell.h"
22 #include "nsINetworkPredictor.h"
23 #include "nsISupportsPriority.h"
24 #include "nsIWebNavigation.h"
25 #include "nsPresContext.h"
27 using namespace mozilla;
28 using namespace mozilla::css;
29 using namespace mozilla::dom;
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)
36 NS_IMPL_ISUPPORTS_INHERITED(FontFaceSetDocumentImpl, FontFaceSetImpl,
37 nsIDOMEventListener, nsICSSLoaderObserver)
39 FontFaceSetDocumentImpl::FontFaceSetDocumentImpl(FontFaceSet* aOwner,
40 dom::Document* aDocument)
41 : FontFaceSetImpl(aOwner), mDocument(aDocument) {}
43 FontFaceSetDocumentImpl::~FontFaceSetDocumentImpl() = default;
45 void FontFaceSetDocumentImpl::Initialize() {
46 RecursiveMutexAutoLock lock(mMutex);
48 MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
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).
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;
68 // Same for the "private browsing" flag.
69 if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
70 mPrivateBrowsing = loadContext->UsePrivateBrowsing();
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();
84 mDocument->CSSLoader()->AddObserver(this);
86 mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>(
87 mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
90 void FontFaceSetDocumentImpl::Destroy() {
91 RemoveDOMContentLoadedListener();
93 if (mDocument && mDocument->CSSLoader()) {
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->CSSLoader()->RemoveObserver(this);
100 mRuleFaces.Clear();
102 // Since the base class may depend on the document remaining set, we need to
103 // clear mDocument after.
104 FontFaceSetImpl::Destroy();
106 mDocument = nullptr;
109 bool FontFaceSetDocumentImpl::IsOnOwningThread() { return NS_IsMainThread(); }
111 #ifdef DEBUG
112 void FontFaceSetDocumentImpl::AssertIsOnOwningThread() {
113 MOZ_ASSERT(NS_IsMainThread());
115 #endif
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)) {}
125 NS_IMETHOD Run() final {
126 mFunc();
127 return NS_OK;
130 private:
131 std::function<void()> mFunc;
134 RefPtr<FontFaceSetDocumentRunnable> runnable =
135 new FontFaceSetDocumentRunnable(aName, std::move(aFunc));
136 NS_DispatchToMainThread(runnable.forget());
139 uint64_t FontFaceSetDocumentImpl::GetInnerWindowID() {
140 MOZ_ASSERT(NS_IsMainThread());
141 if (!mDocument) {
142 return 0;
145 return mDocument->InnerWindowID();
148 nsPresContext* FontFaceSetDocumentImpl::GetPresContext() const {
149 mozilla::AssertIsMainThreadOrServoFontMetricsLocked();
150 if (!mDocument) {
151 return nullptr;
154 return mDocument->GetPresContext();
157 void FontFaceSetDocumentImpl::RefreshStandardFontLoadPrincipal() {
158 MOZ_ASSERT(NS_IsMainThread());
159 RecursiveMutexAutoLock lock(mMutex);
160 if (NS_WARN_IF(!mDocument)) {
161 return;
163 mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>(
164 mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
165 FontFaceSetImpl::RefreshStandardFontLoadPrincipal();
168 already_AddRefed<URLExtraData> FontFaceSetDocumentImpl::GetURLExtraData() {
169 if (!mDocument) {
170 return nullptr;
172 return do_AddRef(mDocument->DefaultStyleAttrURLData());
175 void FontFaceSetDocumentImpl::RemoveDOMContentLoadedListener() {
176 if (mDocument) {
177 mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false);
181 void FontFaceSetDocumentImpl::FindMatchingFontFaces(
182 const nsTHashSet<FontFace*>& aMatchingFaces,
183 nsTArray<FontFace*>& aFontFaces) {
184 FontFaceSetImpl::FindMatchingFontFaces(aMatchingFaces, aFontFaces);
185 for (FontFaceRecord& record : mRuleFaces) {
186 FontFace* owner = record.mFontFace->GetOwner();
187 if (owner && aMatchingFaces.Contains(owner)) {
188 aFontFaces.AppendElement(owner);
193 TimeStamp FontFaceSetDocumentImpl::GetNavigationStartTimeStamp() {
194 TimeStamp navStart;
195 RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
196 if (timing) {
197 navStart = timing->GetNavigationStartTimeStamp();
199 return navStart;
202 void FontFaceSetDocumentImpl::EnsureReady() {
203 MOZ_ASSERT(NS_IsMainThread());
205 // There may be outstanding style changes that will trigger the loading of
206 // new fonts. We need to flush layout to initiate any such loads so that
207 // if mReady is currently resolved we replace it with a new pending Promise.
208 // (That replacement will happen under this flush call.)
209 if (!ReadyPromiseIsPending() && mDocument) {
210 mDocument->FlushPendingNotifications(FlushType::Layout);
214 #ifdef DEBUG
215 bool FontFaceSetDocumentImpl::HasRuleFontFace(FontFaceImpl* aFontFace) {
216 for (size_t i = 0; i < mRuleFaces.Length(); i++) {
217 if (mRuleFaces[i].mFontFace == aFontFace) {
218 return true;
221 return false;
223 #endif
225 bool FontFaceSetDocumentImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
226 if (NS_WARN_IF(!mDocument)) {
227 return false;
230 if (!FontFaceSetImpl::Add(aFontFace, aRv)) {
231 return false;
234 RefPtr<dom::Document> clonedDoc = mDocument->GetLatestStaticClone();
235 if (clonedDoc) {
236 // The document is printing, copy the font to the static clone as well.
237 nsCOMPtr<nsIPrincipal> principal = mDocument->GetPrincipal();
238 if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) {
239 ErrorResult rv;
240 clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv);
241 MOZ_ASSERT(!rv.Failed());
245 return true;
248 nsresult FontFaceSetDocumentImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
249 uint32_t aSrcIndex) {
250 if (NS_WARN_IF(!mDocument)) {
251 return NS_ERROR_FAILURE;
254 nsresult rv;
256 nsCOMPtr<nsIStreamLoader> streamLoader;
257 RefPtr<nsFontFaceLoader> fontLoader;
259 const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex);
261 auto preloadKey =
262 PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS);
263 RefPtr<PreloaderBase> preload =
264 mDocument->Preloads().LookupPreload(preloadKey);
266 if (preload) {
267 fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this,
268 preload->Channel());
269 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
270 fontLoader);
271 NS_ENSURE_SUCCESS(rv, rv);
273 rv = preload->AsyncConsume(streamLoader);
275 // We don't want this to hang around regardless of the result, there will be
276 // no coalescing of later found <link preload> tags for fonts.
277 preload->RemoveSelf(mDocument);
278 } else {
279 // No preload found, open a channel.
280 rv = NS_ERROR_FAILURE;
283 nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
284 if (NS_FAILED(rv)) {
285 nsCOMPtr<nsIChannel> channel;
286 rv = FontLoaderUtils::BuildChannel(
287 getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
288 dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
289 mDocument, loadGroup, nullptr, false,
290 nsISupportsPriority::PRIORITY_HIGH);
291 NS_ENSURE_SUCCESS(rv, rv);
293 fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel);
295 if (LOG_ENABLED()) {
296 nsCOMPtr<nsIURI> referrer = src.mReferrerInfo
297 ? src.mReferrerInfo->GetOriginalReferrer()
298 : nullptr;
299 LOG((
300 "userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
301 fontLoader.get(), src.mURI->GetSpecOrDefault().get(),
302 referrer ? referrer->GetSpecOrDefault().get() : ""));
305 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
306 fontLoader);
307 NS_ENSURE_SUCCESS(rv, rv);
309 rv = channel->AsyncOpen(streamLoader);
310 if (NS_FAILED(rv)) {
311 fontLoader->DropChannel(); // explicitly need to break ref cycle
316 RecursiveMutexAutoLock lock(mMutex);
317 mLoaders.PutEntry(fontLoader);
320 net::PredictorLearn(src.mURI->get(), mDocument->GetDocumentURI(),
321 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup);
323 if (NS_SUCCEEDED(rv)) {
324 fontLoader->StartedLoading(streamLoader);
325 // let the font entry remember the loader, in case we need to cancel it
326 aUserFontEntry->SetLoader(fontLoader);
329 return rv;
332 bool FontFaceSetDocumentImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) {
333 MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL);
335 if (ServoStyleSet::IsInServoTraversal()) {
336 RecursiveMutexAutoLock lock(mMutex);
337 auto entry = mAllowedFontLoads.Lookup(&aSrc);
338 MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?");
339 return entry ? *entry : false;
342 MOZ_ASSERT(NS_IsMainThread());
344 if (aSrc.mUseOriginPrincipal) {
345 return true;
348 if (NS_WARN_IF(!mDocument)) {
349 return false;
352 RefPtr<gfxFontSrcPrincipal> gfxPrincipal =
353 aSrc.mURI->InheritsSecurityContext() ? nullptr
354 : aSrc.LoadPrincipal(*this);
356 nsIPrincipal* principal =
357 gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr;
359 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
360 mDocument->NodePrincipal(), // loading principal
361 principal, // triggering principal
362 mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
363 nsIContentPolicy::TYPE_FONT);
365 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
366 nsresult rv =
367 NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &shouldLoad,
368 nsContentUtils::GetContentPolicy());
370 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
373 nsresult FontFaceSetDocumentImpl::CreateChannelForSyncLoadFontData(
374 nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
375 const gfxFontFaceSrc* aFontFaceSrc) {
376 gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
378 // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
379 // node and a principal. This is because the document where the font is
380 // being loaded might have a different origin from the principal of the
381 // stylesheet that initiated the font load.
382 // Further, we only get here for data: loads, so it doesn't really matter
383 // whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be
384 // more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
385 return NS_NewChannelWithTriggeringPrincipal(
386 aOutChannel, aFontFaceSrc->mURI->get(), mDocument,
387 principal ? principal->NodePrincipal() : nullptr,
388 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
389 aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT
390 : nsIContentPolicy::TYPE_FONT);
393 bool FontFaceSetDocumentImpl::UpdateRules(
394 const nsTArray<nsFontFaceRuleContainer>& aRules) {
395 RecursiveMutexAutoLock lock(mMutex);
397 // If there was a change to the mNonRuleFaces array, then there could
398 // have been a modification to the user font set.
399 bool modified = mNonRuleFacesDirty;
400 mNonRuleFacesDirty = false;
402 // reuse existing FontFace objects mapped to rules already
403 nsTHashMap<nsPtrHashKey<StyleLockedFontFaceRule>, FontFaceImpl*> ruleFaceMap;
404 for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
405 FontFaceImpl* f = mRuleFaces[i].mFontFace;
406 if (!f || !f->GetOwner()) {
407 continue;
409 ruleFaceMap.InsertOrUpdate(f->GetRule(), f);
412 // The @font-face rules that make up the user font set have changed,
413 // so we need to update the set. However, we want to preserve existing
414 // font entries wherever possible, so that we don't discard and then
415 // re-download resources in the (common) case where at least some of the
416 // same rules are still present.
418 nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
420 // Remove faces from the font family records; we need to re-insert them
421 // because we might end up with faces in a different order even if they're
422 // the same font entries as before. (The order can affect font selection
423 // where multiple faces match the requested style, perhaps with overlapping
424 // unicode-range coverage.)
425 for (const auto& fontFamily : mFontFamilies.Values()) {
426 fontFamily->DetachFontEntries();
429 // Sometimes aRules has duplicate @font-face rules in it; we should make
430 // that not happen, but in the meantime, don't try to insert the same
431 // FontFace object more than once into mRuleFaces. We track which
432 // ones we've handled in this table.
433 nsTHashSet<StyleLockedFontFaceRule*> handledRules;
435 for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
436 // Insert each FontFace objects for each rule into our list, migrating old
437 // font entries if possible rather than creating new ones; set modified to
438 // true if we detect that rule ordering has changed, or if a new entry is
439 // created.
440 StyleLockedFontFaceRule* rule = aRules[i].mRule;
441 if (!handledRules.EnsureInserted(rule)) {
442 // rule was already present in the hashtable
443 continue;
445 RefPtr<FontFaceImpl> faceImpl = ruleFaceMap.Get(rule);
446 RefPtr<FontFace> face = faceImpl ? faceImpl->GetOwner() : nullptr;
447 if (mOwner && (!faceImpl || !face)) {
448 face = FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, rule);
449 faceImpl = face->GetImpl();
451 InsertRuleFontFace(faceImpl, face, aRules[i].mOrigin, oldRecords, modified);
454 for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
455 // Do the same for the non rule backed FontFace objects.
456 InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace);
459 // Remove any residual families that have no font entries (i.e., they were
460 // not defined at all by the updated set of @font-face rules).
461 for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
462 if (!it.Data()->FontListLength()) {
463 it.Remove();
467 // If any FontFace objects for rules are left in the old list, note that the
468 // set has changed (even if the new set was built entirely by migrating old
469 // font entries).
470 if (oldRecords.Length() > 0) {
471 modified = true;
472 // Any in-progress loaders for obsolete rules should be cancelled,
473 // as the resource being downloaded will no longer be required.
474 // We need to explicitly remove any loaders here, otherwise the loaders
475 // will keep their "orphaned" font entries alive until they complete,
476 // even after the oldRules array is deleted.
478 // XXX Now that it is possible for the author to hold on to a rule backed
479 // FontFace object, we shouldn't cancel loading here; instead we should do
480 // it when the FontFace is GCed, if we can detect that.
481 size_t count = oldRecords.Length();
482 for (size_t i = 0; i < count; ++i) {
483 RefPtr<FontFaceImpl> f = oldRecords[i].mFontFace;
484 gfxUserFontEntry* userFontEntry = f->GetUserFontEntry();
485 if (userFontEntry) {
486 nsFontFaceLoader* loader = userFontEntry->GetLoader();
487 if (loader) {
488 loader->Cancel();
489 RemoveLoader(loader);
493 // Any left over FontFace objects should also cease being rule backed.
494 f->DisconnectFromRule();
498 if (modified) {
499 IncrementGeneration(true);
500 mHasLoadingFontFacesIsDirty = true;
501 CheckLoadingStarted();
502 CheckLoadingFinished();
505 // if local rules needed to be rebuilt, they have been rebuilt at this point
506 if (mRebuildLocalRules) {
507 mLocalRulesUsed = false;
508 mRebuildLocalRules = false;
511 if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
512 LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this,
513 (modified ? "modified" : "not modified"), (int)(mRuleFaces.Length())));
516 return modified;
519 void FontFaceSetDocumentImpl::InsertRuleFontFace(
520 FontFaceImpl* aFontFace, FontFace* aFontFaceOwner, StyleOrigin aSheetType,
521 nsTArray<FontFaceRecord>& aOldRecords, bool& aFontSetModified) {
522 RecursiveMutexAutoLock lock(mMutex);
524 gfxUserFontAttributes attr;
525 if (!aFontFace->GetAttributes(attr)) {
526 // If there is no family name, this rule cannot contribute a
527 // usable font, so there is no point in processing it further.
528 return;
531 bool remove = false;
532 size_t removeIndex;
534 // This is a rule backed FontFace. First, we check in aOldRecords; if
535 // the FontFace for the rule exists there, just move it to the new record
536 // list, and put the entry into the appropriate family.
537 for (size_t i = 0; i < aOldRecords.Length(); ++i) {
538 FontFaceRecord& rec = aOldRecords[i];
540 if (rec.mFontFace == aFontFace && rec.mOrigin == Some(aSheetType)) {
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 if (aFontFace->HasLocalSrc()) {
545 // Remove the old record, but wait to see if we successfully create a
546 // new user font entry below.
547 remove = true;
548 removeIndex = i;
549 break;
553 gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry();
554 MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now");
556 AddUserFontEntry(attr.mFamilyName, entry);
558 MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
559 "FontFace should not occur in mRuleFaces twice");
561 mRuleFaces.AppendElement(rec);
562 aOldRecords.RemoveElementAt(i);
564 if (mOwner && aFontFaceOwner) {
565 mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
568 // note the set has been modified if an old rule was skipped to find
569 // this one - something has been dropped, or ordering changed
570 if (i > 0) {
571 aFontSetModified = true;
573 return;
577 // this is a new rule:
578 nsAutoCString family(attr.mFamilyName);
579 RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace(
580 aFontFace, std::move(attr), aSheetType);
582 if (!entry) {
583 return;
586 if (remove) {
587 // Although we broke out of the aOldRecords loop above, since we found
588 // src local usage, and we're not using the old user font entry, we still
589 // are adding a record to mRuleFaces with the same FontFace object.
590 // Remove the old record so that we don't have the same FontFace listed
591 // in both mRuleFaces and oldRecords, which would cause us to call
592 // DisconnectFromRule on a FontFace that should still be rule backed.
593 aOldRecords.RemoveElementAt(removeIndex);
596 FontFaceRecord rec;
597 rec.mFontFace = aFontFace;
598 rec.mOrigin = Some(aSheetType);
600 aFontFace->SetUserFontEntry(entry);
602 MOZ_ASSERT(!HasRuleFontFace(aFontFace),
603 "FontFace should not occur in mRuleFaces twice");
605 mRuleFaces.AppendElement(rec);
607 if (mOwner && aFontFaceOwner) {
608 mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
611 // this was a new rule and font entry, so note that the set was modified
612 aFontSetModified = true;
614 // Add the entry to the end of the list. If an existing userfont entry was
615 // returned by FindOrCreateUserFontEntryFromFontFace that was already stored
616 // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
617 // calls, will automatically remove the earlier occurrence of the same
618 // userfont entry.
619 AddUserFontEntry(family, entry);
622 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForEntry(
623 gfxFontEntry* aFontEntry) {
624 NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
625 for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
626 FontFaceImpl* f = mRuleFaces[i].mFontFace;
627 gfxUserFontEntry* entry = f->GetUserFontEntry();
628 if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
629 return f->GetRule();
632 return nullptr;
635 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForUserFontEntry(
636 gfxUserFontEntry* aUserFontEntry) {
637 for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
638 FontFaceImpl* f = mRuleFaces[i].mFontFace;
639 if (f->GetUserFontEntry() == aUserFontEntry) {
640 return f->GetRule();
643 return nullptr;
646 void FontFaceSetDocumentImpl::CacheFontLoadability() {
647 RecursiveMutexAutoLock lock(mMutex);
649 // TODO(emilio): We could do it a bit more incrementally maybe?
650 for (const auto& fontFamily : mFontFamilies.Values()) {
651 fontFamily->ReadLock();
652 for (const gfxFontEntry* entry : fontFamily->GetFontList()) {
653 if (!entry->mIsUserFontContainer) {
654 continue;
657 const auto& sourceList =
658 static_cast<const gfxUserFontEntry*>(entry)->SourceList();
659 for (const gfxFontFaceSrc& src : sourceList) {
660 if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) {
661 continue;
663 mAllowedFontLoads.LookupOrInsertWith(
664 &src, [&] { return IsFontLoadAllowed(src); });
667 fontFamily->ReadUnlock();
671 void FontFaceSetDocumentImpl::DidRefresh() { CheckLoadingFinished(); }
673 void FontFaceSetDocumentImpl::UpdateHasLoadingFontFaces() {
674 RecursiveMutexAutoLock lock(mMutex);
675 FontFaceSetImpl::UpdateHasLoadingFontFaces();
677 if (mHasLoadingFontFaces) {
678 return;
681 for (size_t i = 0; i < mRuleFaces.Length(); i++) {
682 FontFaceImpl* f = mRuleFaces[i].mFontFace;
683 if (f->Status() == FontFaceLoadStatus::Loading) {
684 mHasLoadingFontFaces = true;
685 return;
690 bool FontFaceSetDocumentImpl::MightHavePendingFontLoads() {
691 if (FontFaceSetImpl::MightHavePendingFontLoads()) {
692 return true;
695 // Check for pending restyles or reflows, as they might cause fonts to
696 // load as new styles apply and text runs are rebuilt.
697 nsPresContext* presContext = GetPresContext();
698 if (presContext && presContext->HasPendingRestyleOrReflow()) {
699 return true;
702 if (mDocument) {
703 // We defer resolving mReady until the document as fully loaded.
704 if (!mDocument->DidFireDOMContentLoaded()) {
705 return true;
708 // And we also wait for any CSS style sheets to finish loading, as their
709 // styles might cause new fonts to load.
710 if (mDocument->CSSLoader()->HasPendingLoads()) {
711 return true;
715 return false;
718 // nsIDOMEventListener
720 NS_IMETHODIMP
721 FontFaceSetDocumentImpl::HandleEvent(Event* aEvent) {
722 nsString type;
723 aEvent->GetType(type);
725 if (!type.EqualsLiteral("DOMContentLoaded")) {
726 return NS_ERROR_FAILURE;
729 RemoveDOMContentLoadedListener();
730 CheckLoadingFinished();
732 return NS_OK;
735 // nsICSSLoaderObserver
737 NS_IMETHODIMP
738 FontFaceSetDocumentImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
739 nsresult aStatus) {
740 CheckLoadingFinished();
741 return NS_OK;
744 void FontFaceSetDocumentImpl::FlushUserFontSet() {
745 if (mDocument) {
746 mDocument->FlushUserFontSet();
750 void FontFaceSetDocumentImpl::MarkUserFontSetDirty() {
751 if (mDocument) {
752 // Ensure we trigger at least a style flush, that will eventually flush the
753 // user font set. Otherwise the font loads that that flush may cause could
754 // never be triggered.
755 if (PresShell* presShell = mDocument->GetPresShell()) {
756 presShell->EnsureStyleFlush();
758 mDocument->MarkUserFontSetDirty();
762 #undef LOG_ENABLED
763 #undef LOG