Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / layout / style / FontFaceSetDocumentImpl.cpp
blob8879ee6bfbd938a2ae50e281889e4310912ca31d
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 "FontPreloader.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 "nsIWebNavigation.h"
24 #include "nsPresContext.h"
26 using namespace mozilla;
27 using namespace mozilla::css;
28 using namespace mozilla::dom;
30 #define LOG(args) \
31 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
32 #define LOG_ENABLED() \
33 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
35 NS_IMPL_ISUPPORTS_INHERITED(FontFaceSetDocumentImpl, FontFaceSetImpl,
36 nsIDOMEventListener, nsICSSLoaderObserver)
38 FontFaceSetDocumentImpl::FontFaceSetDocumentImpl(FontFaceSet* aOwner,
39 dom::Document* aDocument)
40 : FontFaceSetImpl(aOwner), mDocument(aDocument) {}
42 FontFaceSetDocumentImpl::~FontFaceSetDocumentImpl() = default;
44 void FontFaceSetDocumentImpl::Initialize() {
45 RecursiveMutexAutoLock lock(mMutex);
47 MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
49 // Record the state of the "bypass cache" flags from the docshell now,
50 // since we want to look at them from style worker threads, and we can
51 // only get to the docshell through a weak pointer (which is only
52 // possible on the main thread).
54 // In theory the load type of a docshell could change after the document
55 // is loaded, but handling that doesn't seem too important.
56 if (nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell()) {
57 uint32_t loadType;
58 uint32_t flags;
59 if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) &&
60 ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) ||
61 (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) &&
62 (flags & nsIRequest::LOAD_BYPASS_CACHE))) {
63 mBypassCache = true;
67 // Same for the "private browsing" flag.
68 if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
69 mPrivateBrowsing = loadContext->UsePrivateBrowsing();
72 if (!mDocument->DidFireDOMContentLoaded()) {
73 mDocument->AddSystemEventListener(u"DOMContentLoaded"_ns, this, false,
74 false);
75 } else {
76 // In some cases we can't rely on CheckLoadingFinished being called from
77 // the refresh driver. For example, documents in display:none iframes.
78 // Or if the document has finished loading and painting at the time that
79 // script requests document.fonts and causes us to get here.
80 CheckLoadingFinished();
83 mDocument->CSSLoader()->AddObserver(this);
85 mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>(
86 mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
89 void FontFaceSetDocumentImpl::Destroy() {
90 RemoveDOMContentLoadedListener();
92 if (mDocument && mDocument->CSSLoader()) {
93 // We're null checking CSSLoader() since FontFaceSetImpl::Disconnect() might
94 // be being called during unlink, at which time the loader may already have
95 // been unlinked from the document.
96 mDocument->CSSLoader()->RemoveObserver(this);
99 mRuleFaces.Clear();
101 // Since the base class may depend on the document remaining set, we need to
102 // clear mDocument after.
103 FontFaceSetImpl::Destroy();
105 mDocument = nullptr;
108 bool FontFaceSetDocumentImpl::IsOnOwningThread() { return NS_IsMainThread(); }
110 #ifdef DEBUG
111 void FontFaceSetDocumentImpl::AssertIsOnOwningThread() {
112 MOZ_ASSERT(NS_IsMainThread());
114 #endif
116 void FontFaceSetDocumentImpl::DispatchToOwningThread(
117 const char* aName, std::function<void()>&& aFunc) {
118 class FontFaceSetDocumentRunnable final : public Runnable {
119 public:
120 FontFaceSetDocumentRunnable(const char* aName,
121 std::function<void()>&& aFunc)
122 : Runnable(aName), mFunc(std::move(aFunc)) {}
124 NS_IMETHOD Run() final {
125 mFunc();
126 return NS_OK;
129 private:
130 std::function<void()> mFunc;
133 RefPtr<FontFaceSetDocumentRunnable> runnable =
134 new FontFaceSetDocumentRunnable(aName, std::move(aFunc));
135 NS_DispatchToMainThread(runnable.forget());
138 uint64_t FontFaceSetDocumentImpl::GetInnerWindowID() {
139 MOZ_ASSERT(NS_IsMainThread());
140 if (!mDocument) {
141 return 0;
144 return mDocument->InnerWindowID();
147 nsPresContext* FontFaceSetDocumentImpl::GetPresContext() const {
148 mozilla::AssertIsMainThreadOrServoFontMetricsLocked();
149 if (!mDocument) {
150 return nullptr;
153 return mDocument->GetPresContext();
156 void FontFaceSetDocumentImpl::RefreshStandardFontLoadPrincipal() {
157 MOZ_ASSERT(NS_IsMainThread());
158 RecursiveMutexAutoLock lock(mMutex);
159 if (NS_WARN_IF(!mDocument)) {
160 return;
162 mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>(
163 mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
164 FontFaceSetImpl::RefreshStandardFontLoadPrincipal();
167 already_AddRefed<URLExtraData> FontFaceSetDocumentImpl::GetURLExtraData() {
168 if (!mDocument) {
169 return nullptr;
171 return do_AddRef(mDocument->DefaultStyleAttrURLData());
174 void FontFaceSetDocumentImpl::RemoveDOMContentLoadedListener() {
175 if (mDocument) {
176 mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false);
180 void FontFaceSetDocumentImpl::FindMatchingFontFaces(
181 const nsTHashSet<FontFace*>& aMatchingFaces,
182 nsTArray<FontFace*>& aFontFaces) {
183 FontFaceSetImpl::FindMatchingFontFaces(aMatchingFaces, aFontFaces);
184 for (FontFaceRecord& record : mRuleFaces) {
185 FontFace* owner = record.mFontFace->GetOwner();
186 if (owner && aMatchingFaces.Contains(owner)) {
187 aFontFaces.AppendElement(owner);
192 TimeStamp FontFaceSetDocumentImpl::GetNavigationStartTimeStamp() {
193 TimeStamp navStart;
194 RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
195 if (timing) {
196 navStart = timing->GetNavigationStartTimeStamp();
198 return navStart;
201 void FontFaceSetDocumentImpl::EnsureReady() {
202 MOZ_ASSERT(NS_IsMainThread());
204 // There may be outstanding style changes that will trigger the loading of
205 // new fonts. We need to flush layout to initiate any such loads so that
206 // if mReady is currently resolved we replace it with a new pending Promise.
207 // (That replacement will happen under this flush call.)
208 if (!ReadyPromiseIsPending() && mDocument) {
209 mDocument->FlushPendingNotifications(FlushType::Layout);
213 #ifdef DEBUG
214 bool FontFaceSetDocumentImpl::HasRuleFontFace(FontFaceImpl* aFontFace) {
215 for (size_t i = 0; i < mRuleFaces.Length(); i++) {
216 if (mRuleFaces[i].mFontFace == aFontFace) {
217 return true;
220 return false;
222 #endif
224 bool FontFaceSetDocumentImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
225 if (NS_WARN_IF(!mDocument)) {
226 return false;
229 if (!FontFaceSetImpl::Add(aFontFace, aRv)) {
230 return false;
233 RefPtr<dom::Document> clonedDoc = mDocument->GetLatestStaticClone();
234 if (clonedDoc) {
235 // The document is printing, copy the font to the static clone as well.
236 nsCOMPtr<nsIPrincipal> principal = mDocument->GetPrincipal();
237 if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) {
238 ErrorResult rv;
239 clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv);
240 MOZ_ASSERT(!rv.Failed());
244 return true;
247 nsresult FontFaceSetDocumentImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
248 uint32_t aSrcIndex) {
249 if (NS_WARN_IF(!mDocument)) {
250 return NS_ERROR_FAILURE;
253 nsresult rv;
255 nsCOMPtr<nsIStreamLoader> streamLoader;
256 RefPtr<nsFontFaceLoader> fontLoader;
258 const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex);
260 auto preloadKey =
261 PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS);
262 RefPtr<PreloaderBase> preload =
263 mDocument->Preloads().LookupPreload(preloadKey);
265 if (preload) {
266 fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this,
267 preload->Channel());
268 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
269 fontLoader);
270 NS_ENSURE_SUCCESS(rv, rv);
272 rv = preload->AsyncConsume(streamLoader);
274 // We don't want this to hang around regardless of the result, there will be
275 // no coalescing of later found <link preload> tags for fonts.
276 preload->RemoveSelf(mDocument);
277 } else {
278 // No preload found, open a channel.
279 rv = NS_ERROR_FAILURE;
282 nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
283 if (NS_FAILED(rv)) {
284 nsCOMPtr<nsIChannel> channel;
285 rv = FontPreloader::BuildChannel(
286 getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
287 dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
288 mDocument, loadGroup, nullptr, false);
289 NS_ENSURE_SUCCESS(rv, rv);
291 fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel);
293 if (LOG_ENABLED()) {
294 nsCOMPtr<nsIURI> referrer = src.mReferrerInfo
295 ? src.mReferrerInfo->GetOriginalReferrer()
296 : nullptr;
297 LOG((
298 "userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
299 fontLoader.get(), src.mURI->GetSpecOrDefault().get(),
300 referrer ? referrer->GetSpecOrDefault().get() : ""));
303 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
304 fontLoader);
305 NS_ENSURE_SUCCESS(rv, rv);
307 rv = channel->AsyncOpen(streamLoader);
308 if (NS_FAILED(rv)) {
309 fontLoader->DropChannel(); // explicitly need to break ref cycle
314 RecursiveMutexAutoLock lock(mMutex);
315 mLoaders.PutEntry(fontLoader);
318 net::PredictorLearn(src.mURI->get(), mDocument->GetDocumentURI(),
319 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup);
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);
327 return rv;
330 bool FontFaceSetDocumentImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) {
331 MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL);
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;
340 MOZ_ASSERT(NS_IsMainThread());
342 if (aSrc.mUseOriginPrincipal) {
343 return true;
346 if (NS_WARN_IF(!mDocument)) {
347 return false;
350 RefPtr<gfxFontSrcPrincipal> gfxPrincipal =
351 aSrc.mURI->InheritsSecurityContext() ? nullptr
352 : aSrc.LoadPrincipal(*this);
354 nsIPrincipal* principal =
355 gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr;
357 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
358 mDocument->NodePrincipal(), // loading principal
359 principal, // triggering principal
360 mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
361 nsIContentPolicy::TYPE_FONT);
363 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
364 nsresult rv = NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo,
365 ""_ns, // mime type
366 &shouldLoad,
367 nsContentUtils::GetContentPolicy());
369 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
372 nsresult FontFaceSetDocumentImpl::CreateChannelForSyncLoadFontData(
373 nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
374 const gfxFontFaceSrc* aFontFaceSrc) {
375 gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
377 // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
378 // node and a principal. This is because the document where the font is
379 // being loaded might have a different origin from the principal of the
380 // stylesheet that initiated the font load.
381 // Further, we only get here for data: loads, so it doesn't really matter
382 // whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be
383 // more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
384 return NS_NewChannelWithTriggeringPrincipal(
385 aOutChannel, aFontFaceSrc->mURI->get(), mDocument,
386 principal ? principal->NodePrincipal() : nullptr,
387 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
388 aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT
389 : nsIContentPolicy::TYPE_FONT);
392 bool FontFaceSetDocumentImpl::UpdateRules(
393 const nsTArray<nsFontFaceRuleContainer>& aRules) {
394 RecursiveMutexAutoLock lock(mMutex);
396 // If there was a change to the mNonRuleFaces array, then there could
397 // have been a modification to the user font set.
398 bool modified = mNonRuleFacesDirty;
399 mNonRuleFacesDirty = false;
401 // reuse existing FontFace objects mapped to rules already
402 nsTHashMap<nsPtrHashKey<StyleLockedFontFaceRule>, FontFaceImpl*> ruleFaceMap;
403 for (size_t i = 0, i_end = mRuleFaces.Length(); i < i_end; ++i) {
404 FontFaceImpl* f = mRuleFaces[i].mFontFace;
405 if (!f || !f->GetOwner()) {
406 continue;
408 ruleFaceMap.InsertOrUpdate(f->GetRule(), f);
411 // The @font-face rules that make up the user font set have changed,
412 // so we need to update the set. However, we want to preserve existing
413 // font entries wherever possible, so that we don't discard and then
414 // re-download resources in the (common) case where at least some of the
415 // same rules are still present.
417 nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
419 // Remove faces from the font family records; we need to re-insert them
420 // because we might end up with faces in a different order even if they're
421 // the same font entries as before. (The order can affect font selection
422 // where multiple faces match the requested style, perhaps with overlapping
423 // unicode-range coverage.)
424 for (const auto& fontFamily : mFontFamilies.Values()) {
425 fontFamily->DetachFontEntries();
428 // Sometimes aRules has duplicate @font-face rules in it; we should make
429 // that not happen, but in the meantime, don't try to insert the same
430 // FontFace object more than once into mRuleFaces. We track which
431 // ones we've handled in this table.
432 nsTHashSet<StyleLockedFontFaceRule*> handledRules;
434 for (size_t i = 0, i_end = aRules.Length(); i < i_end; ++i) {
435 // Insert each FontFace objects for each rule into our list, migrating old
436 // font entries if possible rather than creating new ones; set modified to
437 // true if we detect that rule ordering has changed, or if a new entry is
438 // created.
439 StyleLockedFontFaceRule* rule = aRules[i].mRule;
440 if (!handledRules.EnsureInserted(rule)) {
441 // rule was already present in the hashtable
442 continue;
444 RefPtr<FontFaceImpl> faceImpl = ruleFaceMap.Get(rule);
445 RefPtr<FontFace> face = faceImpl ? faceImpl->GetOwner() : nullptr;
446 if (mOwner && (!faceImpl || !face)) {
447 face = FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, rule);
448 faceImpl = face->GetImpl();
450 InsertRuleFontFace(faceImpl, face, aRules[i].mOrigin, oldRecords, modified);
453 for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
454 // Do the same for the non rule backed FontFace objects.
455 InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace);
458 // Remove any residual families that have no font entries (i.e., they were
459 // not defined at all by the updated set of @font-face rules).
460 for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
461 if (!it.Data()->FontListLength()) {
462 it.Remove();
466 // If any FontFace objects for rules are left in the old list, note that the
467 // set has changed (even if the new set was built entirely by migrating old
468 // font entries).
469 if (oldRecords.Length() > 0) {
470 modified = true;
471 // Any in-progress loaders for obsolete rules should be cancelled,
472 // as the resource being downloaded will no longer be required.
473 // We need to explicitly remove any loaders here, otherwise the loaders
474 // will keep their "orphaned" font entries alive until they complete,
475 // even after the oldRules array is deleted.
477 // XXX Now that it is possible for the author to hold on to a rule backed
478 // FontFace object, we shouldn't cancel loading here; instead we should do
479 // it when the FontFace is GCed, if we can detect that.
480 size_t count = oldRecords.Length();
481 for (size_t i = 0; i < count; ++i) {
482 RefPtr<FontFaceImpl> f = oldRecords[i].mFontFace;
483 gfxUserFontEntry* userFontEntry = f->GetUserFontEntry();
484 if (userFontEntry) {
485 nsFontFaceLoader* loader = userFontEntry->GetLoader();
486 if (loader) {
487 loader->Cancel();
488 RemoveLoader(loader);
492 // Any left over FontFace objects should also cease being rule backed.
493 f->DisconnectFromRule();
497 if (modified) {
498 IncrementGeneration(true);
499 mHasLoadingFontFacesIsDirty = true;
500 CheckLoadingStarted();
501 CheckLoadingFinished();
504 // if local rules needed to be rebuilt, they have been rebuilt at this point
505 if (mRebuildLocalRules) {
506 mLocalRulesUsed = false;
507 mRebuildLocalRules = false;
510 if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
511 LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this,
512 (modified ? "modified" : "not modified"), (int)(mRuleFaces.Length())));
515 return modified;
518 void FontFaceSetDocumentImpl::InsertRuleFontFace(
519 FontFaceImpl* aFontFace, FontFace* aFontFaceOwner, StyleOrigin aSheetType,
520 nsTArray<FontFaceRecord>& aOldRecords, bool& aFontSetModified) {
521 RecursiveMutexAutoLock lock(mMutex);
523 gfxUserFontAttributes attr;
524 if (!aFontFace->GetAttributes(attr)) {
525 // If there is no family name, this rule cannot contribute a
526 // usable font, so there is no point in processing it further.
527 return;
530 bool remove = false;
531 size_t removeIndex;
533 // This is a rule backed FontFace. First, we check in aOldRecords; if
534 // the FontFace for the rule exists there, just move it to the new record
535 // list, and put the entry into the appropriate family.
536 for (size_t i = 0; i < aOldRecords.Length(); ++i) {
537 FontFaceRecord& rec = aOldRecords[i];
539 if (rec.mFontFace == aFontFace && rec.mOrigin == Some(aSheetType)) {
540 // if local rules were used, don't use the old font entry
541 // for rules containing src local usage
542 if (mLocalRulesUsed && mRebuildLocalRules) {
543 if (aFontFace->HasLocalSrc()) {
544 // Remove the old record, but wait to see if we successfully create a
545 // new user font entry below.
546 remove = true;
547 removeIndex = i;
548 break;
552 gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry();
553 MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now");
555 AddUserFontEntry(attr.mFamilyName, entry);
557 MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
558 "FontFace should not occur in mRuleFaces twice");
560 mRuleFaces.AppendElement(rec);
561 aOldRecords.RemoveElementAt(i);
563 if (mOwner && aFontFaceOwner) {
564 mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
567 // note the set has been modified if an old rule was skipped to find
568 // this one - something has been dropped, or ordering changed
569 if (i > 0) {
570 aFontSetModified = true;
572 return;
576 // this is a new rule:
577 nsAutoCString family(attr.mFamilyName);
578 RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace(
579 aFontFace, std::move(attr), aSheetType);
581 if (!entry) {
582 return;
585 if (remove) {
586 // Although we broke out of the aOldRecords loop above, since we found
587 // src local usage, and we're not using the old user font entry, we still
588 // are adding a record to mRuleFaces with the same FontFace object.
589 // Remove the old record so that we don't have the same FontFace listed
590 // in both mRuleFaces and oldRecords, which would cause us to call
591 // DisconnectFromRule on a FontFace that should still be rule backed.
592 aOldRecords.RemoveElementAt(removeIndex);
595 FontFaceRecord rec;
596 rec.mFontFace = aFontFace;
597 rec.mOrigin = Some(aSheetType);
599 aFontFace->SetUserFontEntry(entry);
601 MOZ_ASSERT(!HasRuleFontFace(aFontFace),
602 "FontFace should not occur in mRuleFaces twice");
604 mRuleFaces.AppendElement(rec);
606 if (mOwner && aFontFaceOwner) {
607 mOwner->InsertRuleFontFace(aFontFaceOwner, aSheetType);
610 // this was a new rule and font entry, so note that the set was modified
611 aFontSetModified = true;
613 // Add the entry to the end of the list. If an existing userfont entry was
614 // returned by FindOrCreateUserFontEntryFromFontFace that was already stored
615 // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
616 // calls, will automatically remove the earlier occurrence of the same
617 // userfont entry.
618 AddUserFontEntry(family, entry);
621 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForEntry(
622 gfxFontEntry* aFontEntry) {
623 NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
624 for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
625 FontFaceImpl* f = mRuleFaces[i].mFontFace;
626 gfxUserFontEntry* entry = f->GetUserFontEntry();
627 if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
628 return f->GetRule();
631 return nullptr;
634 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForUserFontEntry(
635 gfxUserFontEntry* aUserFontEntry) {
636 for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
637 FontFaceImpl* f = mRuleFaces[i].mFontFace;
638 if (f->GetUserFontEntry() == aUserFontEntry) {
639 return f->GetRule();
642 return nullptr;
645 void FontFaceSetDocumentImpl::CacheFontLoadability() {
646 RecursiveMutexAutoLock lock(mMutex);
648 // TODO(emilio): We could do it a bit more incrementally maybe?
649 for (const auto& fontFamily : mFontFamilies.Values()) {
650 fontFamily->ReadLock();
651 for (const gfxFontEntry* entry : fontFamily->GetFontList()) {
652 if (!entry->mIsUserFontContainer) {
653 continue;
656 const auto& sourceList =
657 static_cast<const gfxUserFontEntry*>(entry)->SourceList();
658 for (const gfxFontFaceSrc& src : sourceList) {
659 if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) {
660 continue;
662 mAllowedFontLoads.LookupOrInsertWith(
663 &src, [&] { return IsFontLoadAllowed(src); });
666 fontFamily->ReadUnlock();
670 void FontFaceSetDocumentImpl::DidRefresh() { CheckLoadingFinished(); }
672 void FontFaceSetDocumentImpl::UpdateHasLoadingFontFaces() {
673 RecursiveMutexAutoLock lock(mMutex);
674 FontFaceSetImpl::UpdateHasLoadingFontFaces();
676 if (mHasLoadingFontFaces) {
677 return;
680 for (size_t i = 0; i < mRuleFaces.Length(); i++) {
681 FontFaceImpl* f = mRuleFaces[i].mFontFace;
682 if (f->Status() == FontFaceLoadStatus::Loading) {
683 mHasLoadingFontFaces = true;
684 return;
689 bool FontFaceSetDocumentImpl::MightHavePendingFontLoads() {
690 if (FontFaceSetImpl::MightHavePendingFontLoads()) {
691 return true;
694 // Check for pending restyles or reflows, as they might cause fonts to
695 // load as new styles apply and text runs are rebuilt.
696 nsPresContext* presContext = GetPresContext();
697 if (presContext && presContext->HasPendingRestyleOrReflow()) {
698 return true;
701 if (mDocument) {
702 // We defer resolving mReady until the document as fully loaded.
703 if (!mDocument->DidFireDOMContentLoaded()) {
704 return true;
707 // And we also wait for any CSS style sheets to finish loading, as their
708 // styles might cause new fonts to load.
709 if (mDocument->CSSLoader()->HasPendingLoads()) {
710 return true;
714 return false;
717 // nsIDOMEventListener
719 NS_IMETHODIMP
720 FontFaceSetDocumentImpl::HandleEvent(Event* aEvent) {
721 nsString type;
722 aEvent->GetType(type);
724 if (!type.EqualsLiteral("DOMContentLoaded")) {
725 return NS_ERROR_FAILURE;
728 RemoveDOMContentLoadedListener();
729 CheckLoadingFinished();
731 return NS_OK;
734 // nsICSSLoaderObserver
736 NS_IMETHODIMP
737 FontFaceSetDocumentImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
738 nsresult aStatus) {
739 CheckLoadingFinished();
740 return NS_OK;
743 void FontFaceSetDocumentImpl::FlushUserFontSet() {
744 if (mDocument) {
745 mDocument->FlushUserFontSet();
749 void FontFaceSetDocumentImpl::MarkUserFontSetDirty() {
750 if (mDocument) {
751 // Ensure we trigger at least a style flush, that will eventually flush the
752 // user font set. Otherwise the font loads that that flush may cause could
753 // never be triggered.
754 if (PresShell* presShell = mDocument->GetPresShell()) {
755 presShell->EnsureStyleFlush();
757 mDocument->MarkUserFontSetDirty();
761 #undef LOG_ENABLED
762 #undef LOG