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 "FontFaceSet.h"
9 #include "gfxFontConstants.h"
10 #include "gfxFontSrcPrincipal.h"
11 #include "gfxFontSrcURI.h"
12 #include "FontPreloader.h"
13 #include "mozilla/css/Loader.h"
14 #include "mozilla/dom/CSSFontFaceRule.h"
15 #include "mozilla/dom/DocumentInlines.h"
16 #include "mozilla/dom/Event.h"
17 #include "mozilla/dom/FontFaceSetBinding.h"
18 #include "mozilla/dom/FontFaceSetIterator.h"
19 #include "mozilla/dom/FontFaceSetLoadEvent.h"
20 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
21 #include "mozilla/dom/Promise.h"
22 #include "mozilla/FontPropertyTypes.h"
23 #include "mozilla/AsyncEventDispatcher.h"
24 #include "mozilla/BasePrincipal.h"
25 #include "mozilla/Logging.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/PresShell.h"
28 #include "mozilla/PresShellInlines.h"
29 #include "mozilla/ServoBindings.h"
30 #include "mozilla/ServoCSSParser.h"
31 #include "mozilla/ServoStyleSet.h"
32 #include "mozilla/ServoUtils.h"
33 #include "mozilla/Sprintf.h"
34 #include "mozilla/StaticPrefs_layout.h"
35 #include "mozilla/Telemetry.h"
36 #include "mozilla/LoadInfo.h"
37 #include "nsComponentManagerUtils.h"
38 #include "nsContentPolicyUtils.h"
39 #include "nsContentUtils.h"
40 #include "nsDeviceContext.h"
41 #include "nsFontFaceLoader.h"
42 #include "nsIConsoleService.h"
43 #include "nsIContentPolicy.h"
44 #include "nsIDocShell.h"
45 #include "mozilla/dom/Document.h"
46 #include "nsILoadContext.h"
47 #include "nsINetworkPredictor.h"
48 #include "nsIPrincipal.h"
49 #include "nsIWebNavigation.h"
50 #include "nsNetUtil.h"
51 #include "nsIInputStream.h"
52 #include "nsLayoutUtils.h"
53 #include "nsPresContext.h"
54 #include "nsPrintfCString.h"
55 #include "nsUTF8Utils.h"
56 #include "nsDOMNavigationTiming.h"
57 #include "ReferrerInfo.h"
59 using namespace mozilla
;
60 using namespace mozilla::css
;
61 using namespace mozilla::dom
;
64 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
65 #define LOG_ENABLED() \
66 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
68 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet
)
70 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet
,
72 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument
);
73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady
);
74 for (size_t i
= 0; i
< tmp
->mRuleFaces
.Length(); i
++) {
75 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces
[i
].mFontFace
);
77 for (size_t i
= 0; i
< tmp
->mNonRuleFaces
.Length(); i
++) {
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces
[i
].mFontFace
);
80 if (tmp
->mUserFontSet
) {
81 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserFontSet
->mFontFaceSet
);
83 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
85 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet
,
88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument
);
89 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady
);
90 for (size_t i
= 0; i
< tmp
->mRuleFaces
.Length(); i
++) {
91 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces
[i
].mFontFace
);
93 for (size_t i
= 0; i
< tmp
->mNonRuleFaces
.Length(); i
++) {
94 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces
[i
].mFontFace
);
96 if (tmp
->mUserFontSet
) {
97 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet
->mFontFaceSet
);
99 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUserFontSet
);
100 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
102 NS_IMPL_ADDREF_INHERITED(FontFaceSet
, DOMEventTargetHelper
)
103 NS_IMPL_RELEASE_INHERITED(FontFaceSet
, DOMEventTargetHelper
)
105 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet
)
106 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
107 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver
)
108 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
110 FontFaceSet::FontFaceSet(nsPIDOMWindowInner
* aWindow
, dom::Document
* aDocument
)
111 : DOMEventTargetHelper(aWindow
),
112 mDocument(aDocument
),
113 mStandardFontLoadPrincipal(new gfxFontSrcPrincipal(
114 mDocument
->NodePrincipal(), mDocument
->PartitionedPrincipal())),
115 mResolveLazilyCreatedReadyPromise(false),
116 mStatus(FontFaceSetLoadStatus::Loaded
),
117 mNonRuleFacesDirty(false),
118 mHasLoadingFontFaces(false),
119 mHasLoadingFontFacesIsDirty(false),
120 mDelayedLoadCheck(false),
122 mPrivateBrowsing(false) {
123 MOZ_ASSERT(mDocument
, "We should get a valid document from the caller!");
125 // Record the state of the "bypass cache" flags from the docshell now,
126 // since we want to look at them from style worker threads, and we can
127 // only get to the docshell through a weak pointer (which is only
128 // possible on the main thread).
130 // In theory the load type of a docshell could change after the document
131 // is loaded, but handling that doesn't seem too important.
132 if (nsCOMPtr
<nsIDocShell
> docShell
= mDocument
->GetDocShell()) {
135 if ((NS_SUCCEEDED(docShell
->GetLoadType(&loadType
)) &&
136 ((loadType
>> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE
)) ||
137 (NS_SUCCEEDED(docShell
->GetDefaultLoadFlags(&flags
)) &&
138 (flags
& nsIRequest::LOAD_BYPASS_CACHE
))) {
143 // Same for the "private browsing" flag.
144 if (nsCOMPtr
<nsILoadContext
> loadContext
= mDocument
->GetLoadContext()) {
145 mPrivateBrowsing
= loadContext
->UsePrivateBrowsing();
148 if (!mDocument
->DidFireDOMContentLoaded()) {
149 mDocument
->AddSystemEventListener(u
"DOMContentLoaded"_ns
, this, false,
152 // In some cases we can't rely on CheckLoadingFinished being called from
153 // the refresh driver. For example, documents in display:none iframes.
154 // Or if the document has finished loading and painting at the time that
155 // script requests document.fonts and causes us to get here.
156 CheckLoadingFinished();
159 mDocument
->CSSLoader()->AddObserver(this);
161 mUserFontSet
= new UserFontSet(this);
164 FontFaceSet::~FontFaceSet() {
165 // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
166 // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
167 MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
172 JSObject
* FontFaceSet::WrapObject(JSContext
* aContext
,
173 JS::Handle
<JSObject
*> aGivenProto
) {
174 return FontFaceSet_Binding::Wrap(aContext
, this, aGivenProto
);
177 void FontFaceSet::Disconnect() {
178 RemoveDOMContentLoadedListener();
180 if (mDocument
&& mDocument
->CSSLoader()) {
181 // We're null checking CSSLoader() since FontFaceSet::Disconnect() might be
182 // being called during unlink, at which time the loader amy already have
183 // been unlinked from the document.
184 mDocument
->CSSLoader()->RemoveObserver(this);
187 for (auto it
= mLoaders
.Iter(); !it
.Done(); it
.Next()) {
188 it
.Get()->GetKey()->Cancel();
194 void FontFaceSet::RemoveDOMContentLoadedListener() {
196 mDocument
->RemoveSystemEventListener(u
"DOMContentLoaded"_ns
, this, false);
200 void FontFaceSet::ParseFontShorthandForMatching(
201 const nsACString
& aFont
, RefPtr
<SharedFontList
>& aFamilyList
,
202 FontWeight
& aWeight
, FontStretch
& aStretch
, FontSlantStyle
& aStyle
,
204 auto style
= StyleComputedFontStyleDescriptor::Normal();
208 RefPtr
<URLExtraData
> url
= ServoCSSParser::GetURLExtraData(mDocument
);
209 if (!ServoCSSParser::ParseFontShorthandForMatching(aFont
, url
, aFamilyList
,
210 style
, stretch
, weight
)) {
211 aRv
.ThrowSyntaxError("Invalid font shorthand");
216 case StyleComputedFontStyleDescriptor::Tag::Normal
:
217 aStyle
= FontSlantStyle::Normal();
219 case StyleComputedFontStyleDescriptor::Tag::Italic
:
220 aStyle
= FontSlantStyle::Italic();
222 case StyleComputedFontStyleDescriptor::Tag::Oblique
:
223 MOZ_ASSERT(style
.AsOblique()._0
== style
.AsOblique()._1
,
224 "We use ComputedFontStyleDescriptor just for convenience, "
225 "the two values should always match");
226 aStyle
= FontSlantStyle::Oblique(style
.AsOblique()._0
);
230 aWeight
= FontWeight(weight
);
231 aStretch
= FontStretch::FromStyle(stretch
);
234 static bool HasAnyCharacterInUnicodeRange(gfxUserFontEntry
* aEntry
,
235 const nsAString
& aInput
) {
236 const char16_t
* p
= aInput
.Data();
237 const char16_t
* end
= p
+ aInput
.Length();
240 uint32_t c
= UTF16CharEnumerator::NextChar(&p
, end
);
241 if (aEntry
->CharacterInUnicodeRange(c
)) {
248 void FontFaceSet::FindMatchingFontFaces(const nsACString
& aFont
,
249 const nsAString
& aText
,
250 nsTArray
<FontFace
*>& aFontFaces
,
252 RefPtr
<SharedFontList
> familyList
;
255 FontSlantStyle italicStyle
;
256 ParseFontShorthandForMatching(aFont
, familyList
, weight
, stretch
, italicStyle
,
263 style
.style
= italicStyle
;
264 style
.weight
= weight
;
265 style
.stretch
= stretch
;
267 nsTArray
<FontFaceRecord
>* arrays
[2];
268 arrays
[0] = &mNonRuleFaces
;
269 arrays
[1] = &mRuleFaces
;
271 // Set of FontFaces that we want to return.
272 nsTHashtable
<nsPtrHashKey
<FontFace
>> matchingFaces
;
274 for (const FontFamilyName
& fontFamilyName
: familyList
->mNames
) {
275 if (!fontFamilyName
.IsNamed()) {
279 RefPtr
<gfxFontFamily
> family
=
280 mUserFontSet
->LookupFamily(nsAtomCString(fontFamilyName
.mName
));
286 AutoTArray
<gfxFontEntry
*, 4> entries
;
287 family
->FindAllFontsForStyle(style
, entries
);
289 for (gfxFontEntry
* e
: entries
) {
290 FontFace::Entry
* entry
= static_cast<FontFace::Entry
*>(e
);
291 if (HasAnyCharacterInUnicodeRange(entry
, aText
)) {
292 for (FontFace
* f
: entry
->GetFontFaces()) {
293 matchingFaces
.PutEntry(f
);
299 // Add all FontFaces in matchingFaces to aFontFaces, in the order
300 // they appear in the FontFaceSet.
301 for (nsTArray
<FontFaceRecord
>* array
: arrays
) {
302 for (FontFaceRecord
& record
: *array
) {
303 FontFace
* f
= record
.mFontFace
;
304 if (matchingFaces
.Contains(f
)) {
305 aFontFaces
.AppendElement(f
);
311 TimeStamp
FontFaceSet::GetNavigationStartTimeStamp() {
313 RefPtr
<nsDOMNavigationTiming
> timing(mDocument
->GetNavigationTiming());
315 navStart
= timing
->GetNavigationStartTimeStamp();
320 already_AddRefed
<Promise
> FontFaceSet::Load(JSContext
* aCx
,
321 const nsACString
& aFont
,
322 const nsAString
& aText
,
326 nsTArray
<RefPtr
<Promise
>> promises
;
328 nsTArray
<FontFace
*> faces
;
329 FindMatchingFontFaces(aFont
, aText
, faces
, aRv
);
334 for (FontFace
* f
: faces
) {
335 RefPtr
<Promise
> promise
= f
->Load(aRv
);
339 if (!promises
.AppendElement(promise
, fallible
)) {
340 aRv
.Throw(NS_ERROR_FAILURE
);
345 return Promise::All(aCx
, promises
, aRv
);
348 bool FontFaceSet::Check(const nsACString
& aFont
, const nsAString
& aText
,
352 nsTArray
<FontFace
*> faces
;
353 FindMatchingFontFaces(aFont
, aText
, faces
, aRv
);
358 for (FontFace
* f
: faces
) {
359 if (f
->Status() != FontFaceLoadStatus::Loaded
) {
367 bool FontFaceSet::ReadyPromiseIsPending() const {
368 return mReady
? mReady
->State() == Promise::PromiseState::Pending
369 : !mResolveLazilyCreatedReadyPromise
;
372 Promise
* FontFaceSet::GetReady(ErrorResult
& aRv
) {
373 MOZ_ASSERT(NS_IsMainThread());
375 // There may be outstanding style changes that will trigger the loading of
376 // new fonts. We need to flush layout to initiate any such loads so that
377 // if mReady is currently resolved we replace it with a new pending Promise.
378 // (That replacement will happen under this flush call.)
379 if (!ReadyPromiseIsPending() && mDocument
) {
380 mDocument
->FlushPendingNotifications(FlushType::Layout
);
384 nsCOMPtr
<nsIGlobalObject
> global
= GetParentObject();
385 mReady
= Promise::Create(global
, aRv
);
387 aRv
.Throw(NS_ERROR_FAILURE
);
390 if (mResolveLazilyCreatedReadyPromise
) {
391 mReady
->MaybeResolve(this);
392 mResolveLazilyCreatedReadyPromise
= false;
399 FontFaceSetLoadStatus
FontFaceSet::Status() {
405 bool FontFaceSet::HasRuleFontFace(FontFace
* aFontFace
) {
406 for (size_t i
= 0; i
< mRuleFaces
.Length(); i
++) {
407 if (mRuleFaces
[i
].mFontFace
== aFontFace
) {
415 void FontFaceSet::Add(FontFace
& aFontFace
, ErrorResult
& aRv
) {
418 if (aFontFace
.IsInFontFaceSet(this)) {
422 if (aFontFace
.HasRule()) {
423 aRv
.ThrowInvalidModificationError(
424 "Can't add face to FontFaceSet that comes from an @font-face rule");
428 aFontFace
.AddFontFaceSet(this);
431 for (const FontFaceRecord
& rec
: mNonRuleFaces
) {
432 MOZ_ASSERT(rec
.mFontFace
!= &aFontFace
,
433 "FontFace should not occur in mNonRuleFaces twice");
437 FontFaceRecord
* rec
= mNonRuleFaces
.AppendElement();
438 rec
->mFontFace
= &aFontFace
;
439 rec
->mOrigin
= Nothing();
440 rec
->mLoadEventShouldFire
=
441 aFontFace
.Status() == FontFaceLoadStatus::Unloaded
||
442 aFontFace
.Status() == FontFaceLoadStatus::Loading
;
444 mNonRuleFacesDirty
= true;
445 MarkUserFontSetDirty();
446 mHasLoadingFontFacesIsDirty
= true;
447 CheckLoadingStarted();
448 RefPtr
<dom::Document
> clonedDoc
= mDocument
->GetLatestStaticClone();
450 // The document is printing, copy the font to the static clone as well.
451 nsCOMPtr
<nsIPrincipal
> principal
= mDocument
->GetPrincipal();
452 if (principal
->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal
)) {
454 clonedDoc
->Fonts()->Add(aFontFace
, rv
);
455 MOZ_ASSERT(!rv
.Failed());
460 void FontFaceSet::Clear() {
463 if (mNonRuleFaces
.IsEmpty()) {
467 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
468 FontFace
* f
= mNonRuleFaces
[i
].mFontFace
;
469 f
->RemoveFontFaceSet(this);
472 mNonRuleFaces
.Clear();
473 mNonRuleFacesDirty
= true;
474 MarkUserFontSetDirty();
475 mHasLoadingFontFacesIsDirty
= true;
476 CheckLoadingFinished();
479 bool FontFaceSet::Delete(FontFace
& aFontFace
) {
482 if (aFontFace
.HasRule()) {
486 bool removed
= false;
487 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
488 if (mNonRuleFaces
[i
].mFontFace
== &aFontFace
) {
489 mNonRuleFaces
.RemoveElementAt(i
);
498 aFontFace
.RemoveFontFaceSet(this);
500 mNonRuleFacesDirty
= true;
501 MarkUserFontSetDirty();
502 mHasLoadingFontFacesIsDirty
= true;
503 CheckLoadingFinished();
507 bool FontFaceSet::HasAvailableFontFace(FontFace
* aFontFace
) {
508 return aFontFace
->IsInFontFaceSet(this);
511 bool FontFaceSet::Has(FontFace
& aFontFace
) {
514 return HasAvailableFontFace(&aFontFace
);
517 FontFace
* FontFaceSet::GetFontFaceAt(uint32_t aIndex
) {
520 if (aIndex
< mRuleFaces
.Length()) {
521 return mRuleFaces
[aIndex
].mFontFace
;
524 aIndex
-= mRuleFaces
.Length();
525 if (aIndex
< mNonRuleFaces
.Length()) {
526 return mNonRuleFaces
[aIndex
].mFontFace
;
532 uint32_t FontFaceSet::Size() {
535 // Web IDL objects can only expose array index properties up to INT32_MAX.
537 size_t total
= mRuleFaces
.Length() + mNonRuleFaces
.Length();
538 return std::min
<size_t>(total
, INT32_MAX
);
541 already_AddRefed
<FontFaceSetIterator
> FontFaceSet::Entries() {
542 RefPtr
<FontFaceSetIterator
> it
= new FontFaceSetIterator(this, true);
546 already_AddRefed
<FontFaceSetIterator
> FontFaceSet::Values() {
547 RefPtr
<FontFaceSetIterator
> it
= new FontFaceSetIterator(this, false);
551 void FontFaceSet::ForEach(JSContext
* aCx
, FontFaceSetForEachCallback
& aCallback
,
552 JS::Handle
<JS::Value
> aThisArg
, ErrorResult
& aRv
) {
553 JS::Rooted
<JS::Value
> thisArg(aCx
, aThisArg
);
554 for (size_t i
= 0; i
< Size(); i
++) {
555 RefPtr
<FontFace
> face
= GetFontFaceAt(i
);
556 aCallback
.Call(thisArg
, *face
, *face
, *this, aRv
);
563 void FontFaceSet::RemoveLoader(nsFontFaceLoader
* aLoader
) {
564 mLoaders
.RemoveEntry(aLoader
);
567 nsresult
FontFaceSet::StartLoad(gfxUserFontEntry
* aUserFontEntry
,
568 const gfxFontFaceSrc
* aFontFaceSrc
) {
571 nsCOMPtr
<nsIStreamLoader
> streamLoader
;
572 RefPtr
<nsFontFaceLoader
> fontLoader
;
575 PreloadHashKey::CreateAsFont(aFontFaceSrc
->mURI
->get(), CORS_ANONYMOUS
);
576 RefPtr
<PreloaderBase
> preload
=
577 mDocument
->Preloads().LookupPreload(preloadKey
);
580 fontLoader
= new nsFontFaceLoader(aUserFontEntry
, aFontFaceSrc
->mURI
->get(),
581 this, preload
->Channel());
583 rv
= NS_NewStreamLoader(getter_AddRefs(streamLoader
), fontLoader
,
585 NS_ENSURE_SUCCESS(rv
, rv
);
587 rv
= preload
->AsyncConsume(streamLoader
);
589 // We don't want this to hang around regardless of the result, there will be
590 // no coalescing of later found <link preload> tags for fonts.
591 preload
->RemoveSelf(mDocument
);
593 // No preload found, open a channel.
594 rv
= NS_ERROR_FAILURE
;
597 nsCOMPtr
<nsILoadGroup
> loadGroup(mDocument
->GetDocumentLoadGroup());
599 nsCOMPtr
<nsIChannel
> channel
;
600 rv
= FontPreloader::BuildChannel(
601 getter_AddRefs(channel
), aFontFaceSrc
->mURI
->get(), CORS_ANONYMOUS
,
602 dom::ReferrerPolicy::_empty
/* not used */, aUserFontEntry
,
603 aFontFaceSrc
, mDocument
, loadGroup
, nullptr, false);
604 NS_ENSURE_SUCCESS(rv
, rv
);
606 fontLoader
= new nsFontFaceLoader(aUserFontEntry
, aFontFaceSrc
->mURI
->get(),
610 nsCOMPtr
<nsIURI
> referrer
=
611 aFontFaceSrc
->mReferrerInfo
612 ? aFontFaceSrc
->mReferrerInfo
->GetOriginalReferrer()
615 "userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
616 fontLoader
.get(), aFontFaceSrc
->mURI
->GetSpecOrDefault().get(),
617 referrer
? referrer
->GetSpecOrDefault().get() : ""));
620 rv
= NS_NewStreamLoader(getter_AddRefs(streamLoader
), fontLoader
,
622 NS_ENSURE_SUCCESS(rv
, rv
);
624 rv
= channel
->AsyncOpen(streamLoader
);
626 fontLoader
->DropChannel(); // explicitly need to break ref cycle
630 mLoaders
.PutEntry(fontLoader
);
632 net::PredictorLearn(aFontFaceSrc
->mURI
->get(), mDocument
->GetDocumentURI(),
633 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
, loadGroup
);
635 if (NS_SUCCEEDED(rv
)) {
636 fontLoader
->StartedLoading(streamLoader
);
637 // let the font entry remember the loader, in case we need to cancel it
638 aUserFontEntry
->SetLoader(fontLoader
);
644 bool FontFaceSet::UpdateRules(const nsTArray
<nsFontFaceRuleContainer
>& aRules
) {
645 MOZ_ASSERT(mUserFontSet
);
647 // If there was a change to the mNonRuleFaces array, then there could
648 // have been a modification to the user font set.
649 bool modified
= mNonRuleFacesDirty
;
650 mNonRuleFacesDirty
= false;
652 // reuse existing FontFace objects mapped to rules already
653 nsTHashMap
<nsPtrHashKey
<RawServoFontFaceRule
>, FontFace
*> ruleFaceMap
;
654 for (size_t i
= 0, i_end
= mRuleFaces
.Length(); i
< i_end
; ++i
) {
655 FontFace
* f
= mRuleFaces
[i
].mFontFace
;
659 ruleFaceMap
.InsertOrUpdate(f
->GetRule(), f
);
662 // The @font-face rules that make up the user font set have changed,
663 // so we need to update the set. However, we want to preserve existing
664 // font entries wherever possible, so that we don't discard and then
665 // re-download resources in the (common) case where at least some of the
666 // same rules are still present.
668 nsTArray
<FontFaceRecord
> oldRecords
= std::move(mRuleFaces
);
670 // Remove faces from the font family records; we need to re-insert them
671 // because we might end up with faces in a different order even if they're
672 // the same font entries as before. (The order can affect font selection
673 // where multiple faces match the requested style, perhaps with overlapping
674 // unicode-range coverage.)
675 for (auto it
= mUserFontSet
->mFontFamilies
.Iter(); !it
.Done(); it
.Next()) {
676 it
.Data()->DetachFontEntries();
679 // Sometimes aRules has duplicate @font-face rules in it; we should make
680 // that not happen, but in the meantime, don't try to insert the same
681 // FontFace object more than once into mRuleFaces. We track which
682 // ones we've handled in this table.
683 nsTHashtable
<nsPtrHashKey
<RawServoFontFaceRule
>> handledRules
;
685 for (size_t i
= 0, i_end
= aRules
.Length(); i
< i_end
; ++i
) {
686 // Insert each FontFace objects for each rule into our list, migrating old
687 // font entries if possible rather than creating new ones; set modified to
688 // true if we detect that rule ordering has changed, or if a new entry is
690 RawServoFontFaceRule
* rule
= aRules
[i
].mRule
;
691 if (!handledRules
.EnsureInserted(rule
)) {
692 // rule was already present in the hashtable
695 RefPtr
<FontFace
> f
= ruleFaceMap
.Get(rule
);
697 f
= FontFace::CreateForRule(GetParentObject(), this, rule
);
699 InsertRuleFontFace(f
, aRules
[i
].mOrigin
, oldRecords
, modified
);
702 for (size_t i
= 0, i_end
= mNonRuleFaces
.Length(); i
< i_end
; ++i
) {
703 // Do the same for the non rule backed FontFace objects.
704 InsertNonRuleFontFace(mNonRuleFaces
[i
].mFontFace
, modified
);
707 // Remove any residual families that have no font entries (i.e., they were
708 // not defined at all by the updated set of @font-face rules).
709 for (auto it
= mUserFontSet
->mFontFamilies
.Iter(); !it
.Done(); it
.Next()) {
710 if (it
.Data()->GetFontList().IsEmpty()) {
715 // If any FontFace objects for rules are left in the old list, note that the
716 // set has changed (even if the new set was built entirely by migrating old
718 if (oldRecords
.Length() > 0) {
720 // Any in-progress loaders for obsolete rules should be cancelled,
721 // as the resource being downloaded will no longer be required.
722 // We need to explicitly remove any loaders here, otherwise the loaders
723 // will keep their "orphaned" font entries alive until they complete,
724 // even after the oldRules array is deleted.
726 // XXX Now that it is possible for the author to hold on to a rule backed
727 // FontFace object, we shouldn't cancel loading here; instead we should do
728 // it when the FontFace is GCed, if we can detect that.
729 size_t count
= oldRecords
.Length();
730 for (size_t i
= 0; i
< count
; ++i
) {
731 RefPtr
<FontFace
> f
= oldRecords
[i
].mFontFace
;
732 gfxUserFontEntry
* userFontEntry
= f
->GetUserFontEntry();
734 nsFontFaceLoader
* loader
= userFontEntry
->GetLoader();
737 RemoveLoader(loader
);
741 // Any left over FontFace objects should also cease being rule backed.
742 f
->DisconnectFromRule();
747 IncrementGeneration(true);
748 mHasLoadingFontFacesIsDirty
= true;
749 CheckLoadingStarted();
750 CheckLoadingFinished();
753 // if local rules needed to be rebuilt, they have been rebuilt at this point
754 if (mUserFontSet
->mRebuildLocalRules
) {
755 mUserFontSet
->mLocalRulesUsed
= false;
756 mUserFontSet
->mRebuildLocalRules
= false;
759 if (LOG_ENABLED() && !mRuleFaces
.IsEmpty()) {
760 LOG(("userfonts (%p) userfont rules update (%s) rule count: %d",
761 mUserFontSet
.get(), (modified
? "modified" : "not modified"),
762 (int)(mRuleFaces
.Length())));
768 void FontFaceSet::IncrementGeneration(bool aIsRebuild
) {
769 MOZ_ASSERT(mUserFontSet
);
770 mUserFontSet
->IncrementGeneration(aIsRebuild
);
773 void FontFaceSet::InsertNonRuleFontFace(FontFace
* aFontFace
,
774 bool& aFontSetModified
) {
775 nsAtom
* fontFamily
= aFontFace
->GetFamilyName();
777 // If there is no family name, this rule cannot contribute a
778 // usable font, so there is no point in processing it further.
782 nsAtomCString
family(fontFamily
);
784 // Just create a new font entry if we haven't got one already.
785 if (!aFontFace
->GetUserFontEntry()) {
786 // XXX Should we be checking mUserFontSet->mLocalRulesUsed like
787 // InsertRuleFontFace does?
788 RefPtr
<gfxUserFontEntry
> entry
= FindOrCreateUserFontEntryFromFontFace(
789 family
, aFontFace
, StyleOrigin::Author
);
793 aFontFace
->SetUserFontEntry(entry
);
796 aFontSetModified
= true;
797 mUserFontSet
->AddUserFontEntry(family
, aFontFace
->GetUserFontEntry());
800 void FontFaceSet::InsertRuleFontFace(FontFace
* aFontFace
,
801 StyleOrigin aSheetType
,
802 nsTArray
<FontFaceRecord
>& aOldRecords
,
803 bool& aFontSetModified
) {
804 nsAtom
* fontFamily
= aFontFace
->GetFamilyName();
806 // If there is no family name, this rule cannot contribute a
807 // usable font, so there is no point in processing it further.
814 nsAtomCString
family(fontFamily
);
816 // This is a rule backed FontFace. First, we check in aOldRecords; if
817 // the FontFace for the rule exists there, just move it to the new record
818 // list, and put the entry into the appropriate family.
819 for (size_t i
= 0; i
< aOldRecords
.Length(); ++i
) {
820 FontFaceRecord
& rec
= aOldRecords
[i
];
822 if (rec
.mFontFace
== aFontFace
&& rec
.mOrigin
== Some(aSheetType
)) {
823 // if local rules were used, don't use the old font entry
824 // for rules containing src local usage
825 if (mUserFontSet
->mLocalRulesUsed
&& mUserFontSet
->mRebuildLocalRules
) {
826 if (aFontFace
->HasLocalSrc()) {
827 // Remove the old record, but wait to see if we successfully create a
828 // new user font entry below.
835 gfxUserFontEntry
* entry
= rec
.mFontFace
->GetUserFontEntry();
836 MOZ_ASSERT(entry
, "FontFace should have a gfxUserFontEntry by now");
838 mUserFontSet
->AddUserFontEntry(family
, entry
);
840 MOZ_ASSERT(!HasRuleFontFace(rec
.mFontFace
),
841 "FontFace should not occur in mRuleFaces twice");
843 mRuleFaces
.AppendElement(rec
);
844 aOldRecords
.RemoveElementAt(i
);
845 // note the set has been modified if an old rule was skipped to find
846 // this one - something has been dropped, or ordering changed
848 aFontSetModified
= true;
854 // this is a new rule:
855 RefPtr
<gfxUserFontEntry
> entry
=
856 FindOrCreateUserFontEntryFromFontFace(family
, aFontFace
, aSheetType
);
863 // Although we broke out of the aOldRecords loop above, since we found
864 // src local usage, and we're not using the old user font entry, we still
865 // are adding a record to mRuleFaces with the same FontFace object.
866 // Remove the old record so that we don't have the same FontFace listed
867 // in both mRuleFaces and oldRecords, which would cause us to call
868 // DisconnectFromRule on a FontFace that should still be rule backed.
869 aOldRecords
.RemoveElementAt(removeIndex
);
873 rec
.mFontFace
= aFontFace
;
874 rec
.mOrigin
= Some(aSheetType
);
875 rec
.mLoadEventShouldFire
=
876 aFontFace
->Status() == FontFaceLoadStatus::Unloaded
||
877 aFontFace
->Status() == FontFaceLoadStatus::Loading
;
879 aFontFace
->SetUserFontEntry(entry
);
881 MOZ_ASSERT(!HasRuleFontFace(aFontFace
),
882 "FontFace should not occur in mRuleFaces twice");
884 mRuleFaces
.AppendElement(rec
);
886 // this was a new rule and font entry, so note that the set was modified
887 aFontSetModified
= true;
889 // Add the entry to the end of the list. If an existing userfont entry was
890 // returned by FindOrCreateUserFontEntryFromFontFace that was already stored
891 // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
892 // calls, will automatically remove the earlier occurrence of the same
894 mUserFontSet
->AddUserFontEntry(family
, entry
);
898 already_AddRefed
<gfxUserFontEntry
>
899 FontFaceSet::FindOrCreateUserFontEntryFromFontFace(FontFace
* aFontFace
) {
900 nsAtom
* fontFamily
= aFontFace
->GetFamilyName();
902 // If there is no family name, this rule cannot contribute a
903 // usable font, so there is no point in processing it further.
907 return FindOrCreateUserFontEntryFromFontFace(nsAtomCString(fontFamily
),
908 aFontFace
, StyleOrigin::Author
);
911 static WeightRange
GetWeightRangeForDescriptor(
912 const Maybe
<StyleComputedFontWeightRange
>& aVal
,
913 gfxFontEntry::RangeFlags
& aRangeFlags
) {
915 aRangeFlags
|= gfxFontEntry::RangeFlags::eAutoWeight
;
916 return WeightRange(FontWeight::Normal());
918 return WeightRange(FontWeight(aVal
->_0
), FontWeight(aVal
->_1
));
921 static SlantStyleRange
GetStyleRangeForDescriptor(
922 const Maybe
<StyleComputedFontStyleDescriptor
>& aVal
,
923 gfxFontEntry::RangeFlags
& aRangeFlags
) {
925 aRangeFlags
|= gfxFontEntry::RangeFlags::eAutoSlantStyle
;
926 return SlantStyleRange(FontSlantStyle::Normal());
930 case StyleComputedFontStyleDescriptor::Tag::Normal
:
931 return SlantStyleRange(FontSlantStyle::Normal());
932 case StyleComputedFontStyleDescriptor::Tag::Italic
:
933 return SlantStyleRange(FontSlantStyle::Italic());
934 case StyleComputedFontStyleDescriptor::Tag::Oblique
:
935 return SlantStyleRange(FontSlantStyle::Oblique(val
.AsOblique()._0
),
936 FontSlantStyle::Oblique(val
.AsOblique()._1
));
938 MOZ_ASSERT_UNREACHABLE("How?");
939 return SlantStyleRange(FontSlantStyle::Normal());
942 static StretchRange
GetStretchRangeForDescriptor(
943 const Maybe
<StyleComputedFontStretchRange
>& aVal
,
944 gfxFontEntry::RangeFlags
& aRangeFlags
) {
946 aRangeFlags
|= gfxFontEntry::RangeFlags::eAutoStretch
;
947 return StretchRange(FontStretch::Normal());
949 return StretchRange(FontStretch::FromStyle(aVal
->_0
),
950 FontStretch::FromStyle(aVal
->_1
));
953 // TODO(emilio): Should this take an nsAtom* aFamilyName instead?
955 // All callers have one handy.
957 already_AddRefed
<gfxUserFontEntry
>
958 FontFaceSet::FindOrCreateUserFontEntryFromFontFace(
959 const nsACString
& aFamilyName
, FontFace
* aFontFace
, StyleOrigin aOrigin
) {
960 FontFaceSet
* set
= aFontFace
->GetPrimaryFontFaceSet();
962 uint32_t languageOverride
= NO_FONT_LANGUAGE_OVERRIDE
;
963 StyleFontDisplay fontDisplay
= StyleFontDisplay::Auto
;
965 gfxFontEntry::RangeFlags rangeFlags
= gfxFontEntry::RangeFlags::eNoFlags
;
969 GetWeightRangeForDescriptor(aFontFace
->GetFontWeight(), rangeFlags
);
972 StretchRange stretch
=
973 GetStretchRangeForDescriptor(aFontFace
->GetFontStretch(), rangeFlags
);
976 SlantStyleRange italicStyle
=
977 GetStyleRangeForDescriptor(aFontFace
->GetFontStyle(), rangeFlags
);
979 // set up font display
980 if (Maybe
<StyleFontDisplay
> display
= aFontFace
->GetFontDisplay()) {
981 fontDisplay
= *display
;
984 // set up font features
985 nsTArray
<gfxFontFeature
> featureSettings
;
986 aFontFace
->GetFontFeatureSettings(featureSettings
);
988 // set up font variations
989 nsTArray
<gfxFontVariation
> variationSettings
;
990 aFontFace
->GetFontVariationSettings(variationSettings
);
992 // set up font language override
993 if (Maybe
<StyleFontLanguageOverride
> descriptor
=
994 aFontFace
->GetFontLanguageOverride()) {
995 languageOverride
= descriptor
->_0
;
998 // set up unicode-range
999 gfxCharacterMap
* unicodeRanges
= aFontFace
->GetUnicodeRangeAsCharacterMap();
1001 RefPtr
<gfxUserFontEntry
> existingEntry
= aFontFace
->GetUserFontEntry();
1002 if (existingEntry
) {
1003 // aFontFace already has a user font entry, so we update its attributes
1004 // rather than creating a new one.
1005 existingEntry
->UpdateAttributes(
1006 weight
, stretch
, italicStyle
, featureSettings
, variationSettings
,
1007 languageOverride
, unicodeRanges
, fontDisplay
, rangeFlags
);
1008 // If the family name has changed, remove the entry from its current family
1009 // and clear the mFamilyName field so it can be reset when added to a new
1011 if (!existingEntry
->mFamilyName
.IsEmpty() &&
1012 existingEntry
->mFamilyName
!= aFamilyName
) {
1013 gfxUserFontFamily
* family
=
1014 set
->GetUserFontSet()->LookupFamily(existingEntry
->mFamilyName
);
1016 family
->RemoveFontEntry(existingEntry
);
1018 existingEntry
->mFamilyName
.Truncate(0);
1020 return existingEntry
.forget();
1024 nsTArray
<gfxFontFaceSrc
> srcArray
;
1026 if (aFontFace
->HasFontData()) {
1027 gfxFontFaceSrc
* face
= srcArray
.AppendElement();
1032 face
->mSourceType
= gfxFontFaceSrc::eSourceType_Buffer
;
1033 face
->mBuffer
= aFontFace
->CreateBufferSource();
1035 AutoTArray
<StyleFontFaceSourceListComponent
, 8> sourceListComponents
;
1036 aFontFace
->GetSources(sourceListComponents
);
1037 size_t len
= sourceListComponents
.Length();
1038 for (size_t i
= 0; i
< len
; ++i
) {
1039 gfxFontFaceSrc
* face
= srcArray
.AppendElement();
1040 const auto& component
= sourceListComponents
[i
];
1041 switch (component
.tag
) {
1042 case StyleFontFaceSourceListComponent::Tag::Local
: {
1043 nsAtom
* atom
= component
.AsLocal();
1044 face
->mLocalName
.Append(nsAtomCString(atom
));
1045 face
->mSourceType
= gfxFontFaceSrc::eSourceType_Local
;
1046 face
->mURI
= nullptr;
1047 face
->mFormatFlags
= 0;
1050 case StyleFontFaceSourceListComponent::Tag::Url
: {
1051 face
->mSourceType
= gfxFontFaceSrc::eSourceType_URL
;
1052 const StyleCssUrl
* url
= component
.AsUrl();
1053 nsIURI
* uri
= url
->GetURI();
1054 face
->mURI
= uri
? new gfxFontSrcURI(uri
) : nullptr;
1055 const URLExtraData
& extraData
= url
->ExtraData();
1056 face
->mReferrerInfo
= extraData
.ReferrerInfo();
1057 face
->mOriginPrincipal
= new gfxFontSrcPrincipal(
1058 extraData
.Principal(), extraData
.Principal());
1060 // agent and user stylesheets are treated slightly differently,
1061 // the same-site origin check and access control headers are
1062 // enforced against the sheet principal rather than the document
1063 // principal to allow user stylesheets to include @font-face rules
1064 face
->mUseOriginPrincipal
=
1065 aOrigin
== StyleOrigin::User
|| aOrigin
== StyleOrigin::UserAgent
;
1067 face
->mLocalName
.Truncate();
1068 face
->mFormatFlags
= 0;
1070 while (i
+ 1 < len
) {
1071 const auto& maybeFontFormat
= sourceListComponents
[i
+ 1];
1072 if (maybeFontFormat
.tag
!=
1073 StyleFontFaceSourceListComponent::Tag::FormatHint
) {
1077 nsDependentCSubstring
valueString(
1078 reinterpret_cast<const char*>(
1079 maybeFontFormat
.format_hint
.utf8_bytes
),
1080 maybeFontFormat
.format_hint
.length
);
1082 if (valueString
.LowerCaseEqualsASCII("woff")) {
1083 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_WOFF
;
1084 } else if (valueString
.LowerCaseEqualsASCII("woff2")) {
1085 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_WOFF2
;
1086 } else if (valueString
.LowerCaseEqualsASCII("opentype")) {
1087 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_OPENTYPE
;
1088 } else if (valueString
.LowerCaseEqualsASCII("truetype")) {
1089 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_TRUETYPE
;
1090 } else if (valueString
.LowerCaseEqualsASCII("truetype-aat")) {
1091 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT
;
1092 } else if (valueString
.LowerCaseEqualsASCII("embedded-opentype")) {
1093 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_EOT
;
1094 } else if (valueString
.LowerCaseEqualsASCII("svg")) {
1095 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_SVG
;
1096 } else if (StaticPrefs::layout_css_font_variations_enabled() &&
1097 valueString
.LowerCaseEqualsASCII("woff-variations")) {
1098 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_WOFF_VARIATIONS
;
1099 } else if (StaticPrefs::layout_css_font_variations_enabled() &&
1100 valueString
.LowerCaseEqualsASCII("woff2-variations")) {
1101 face
->mFormatFlags
|=
1102 gfxUserFontSet::FLAG_FORMAT_WOFF2_VARIATIONS
;
1103 } else if (StaticPrefs::layout_css_font_variations_enabled() &&
1104 valueString
.LowerCaseEqualsASCII(
1105 "opentype-variations")) {
1106 face
->mFormatFlags
|=
1107 gfxUserFontSet::FLAG_FORMAT_OPENTYPE_VARIATIONS
;
1108 } else if (StaticPrefs::layout_css_font_variations_enabled() &&
1109 valueString
.LowerCaseEqualsASCII(
1110 "truetype-variations")) {
1111 face
->mFormatFlags
|=
1112 gfxUserFontSet::FLAG_FORMAT_TRUETYPE_VARIATIONS
;
1114 // unknown format specified, mark to distinguish from the
1115 // case where no format hints are specified
1116 face
->mFormatFlags
|= gfxUserFontSet::FLAG_FORMAT_UNKNOWN
;
1121 // if URI not valid, omit from src array
1122 srcArray
.RemoveLastElement();
1123 NS_WARNING("null url in @font-face rule");
1128 case StyleFontFaceSourceListComponent::Tag::FormatHint
:
1129 MOZ_ASSERT_UNREACHABLE(
1130 "Should always come after a URL source, and be consumed already");
1136 if (srcArray
.IsEmpty()) {
1140 RefPtr
<gfxUserFontEntry
> entry
= set
->mUserFontSet
->FindOrCreateUserFontEntry(
1141 aFamilyName
, srcArray
, weight
, stretch
, italicStyle
, featureSettings
,
1142 variationSettings
, languageOverride
, unicodeRanges
, fontDisplay
,
1145 return entry
.forget();
1148 RawServoFontFaceRule
* FontFaceSet::FindRuleForEntry(gfxFontEntry
* aFontEntry
) {
1149 NS_ASSERTION(!aFontEntry
->mIsUserFontContainer
, "only platform font entries");
1150 for (uint32_t i
= 0; i
< mRuleFaces
.Length(); ++i
) {
1151 FontFace
* f
= mRuleFaces
[i
].mFontFace
;
1152 gfxUserFontEntry
* entry
= f
->GetUserFontEntry();
1153 if (entry
&& entry
->GetPlatformFontEntry() == aFontEntry
) {
1154 return f
->GetRule();
1160 RawServoFontFaceRule
* FontFaceSet::FindRuleForUserFontEntry(
1161 gfxUserFontEntry
* aUserFontEntry
) {
1162 for (uint32_t i
= 0; i
< mRuleFaces
.Length(); ++i
) {
1163 FontFace
* f
= mRuleFaces
[i
].mFontFace
;
1164 if (f
->GetUserFontEntry() == aUserFontEntry
) {
1165 return f
->GetRule();
1171 nsresult
FontFaceSet::LogMessage(gfxUserFontEntry
* aUserFontEntry
,
1172 const char* aMessage
, uint32_t aFlags
,
1174 MOZ_ASSERT(NS_IsMainThread() ||
1175 ServoStyleSet::IsCurrentThreadInServoTraversal());
1177 nsCOMPtr
<nsIConsoleService
> console(
1178 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
1180 return NS_ERROR_NOT_AVAILABLE
;
1183 nsAutoCString familyName
;
1184 nsAutoCString fontURI
;
1185 aUserFontEntry
->GetFamilyNameAndURIForLogging(familyName
, fontURI
);
1187 nsAutoCString weightString
;
1188 aUserFontEntry
->Weight().ToString(weightString
);
1189 nsAutoCString stretchString
;
1190 aUserFontEntry
->Stretch().ToString(stretchString
);
1191 nsPrintfCString
message(
1192 "downloadable font: %s "
1193 "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
1194 aMessage
, familyName
.get(),
1195 aUserFontEntry
->IsItalic() ? "italic" : "normal", // XXX todo: oblique?
1196 weightString
.get(), stretchString
.get(), aUserFontEntry
->GetSrcIndex());
1198 if (NS_FAILED(aStatus
)) {
1199 message
.AppendLiteral(": ");
1201 case NS_ERROR_DOM_BAD_URI
:
1202 message
.AppendLiteral("bad URI or cross-site access not allowed");
1204 case NS_ERROR_CONTENT_BLOCKED
:
1205 message
.AppendLiteral("content blocked");
1208 message
.AppendLiteral("status=");
1209 message
.AppendInt(static_cast<uint32_t>(aStatus
));
1213 message
.AppendLiteral(" source: ");
1214 message
.Append(fontURI
);
1216 LOG(("userfonts (%p) %s", mUserFontSet
.get(), message
.get()));
1218 // try to give the user an indication of where the rule came from
1219 RawServoFontFaceRule
* rule
= FindRuleForUserFontEntry(aUserFontEntry
);
1223 uint32_t column
= 0;
1225 Servo_FontFaceRule_GetCssText(rule
, &text
);
1226 Servo_FontFaceRule_GetSourceLocation(rule
, &line
, &column
);
1227 // FIXME We need to figure out an approach to get the style sheet
1228 // of this raw rule. See bug 1450903.
1230 StyleSheet
* sheet
= rule
->GetStyleSheet();
1231 // if the style sheet is removed while the font is loading can be null
1233 nsCString spec
= sheet
->GetSheetURI()->GetSpecOrDefault();
1234 CopyUTF8toUTF16(spec
, href
);
1236 NS_WARNING("null parent stylesheet for @font-face rule");
1237 href
.AssignLiteral("unknown");
1240 // Leave href empty if we don't know how to get the correct sheet.
1244 nsCOMPtr
<nsIScriptError
> scriptError
=
1245 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
);
1246 NS_ENSURE_SUCCESS(rv
, rv
);
1248 uint64_t innerWindowID
= mDocument
->InnerWindowID();
1249 rv
= scriptError
->InitWithWindowID(NS_ConvertUTF8toUTF16(message
),
1251 NS_ConvertUTF8toUTF16(text
), // src line
1254 "CSS Loader", // category (make separate?)
1256 if (NS_SUCCEEDED(rv
)) {
1257 console
->LogMessage(scriptError
);
1263 void FontFaceSet::CacheFontLoadability() {
1264 if (!mUserFontSet
) {
1268 // TODO(emilio): We could do it a bit more incrementally maybe?
1269 for (auto iter
= mUserFontSet
->mFontFamilies
.Iter(); !iter
.Done();
1271 for (const gfxFontEntry
* entry
: iter
.Data()->GetFontList()) {
1272 if (!entry
->mIsUserFontContainer
) {
1276 const auto& sourceList
=
1277 static_cast<const gfxUserFontEntry
*>(entry
)->SourceList();
1278 for (const gfxFontFaceSrc
& src
: sourceList
) {
1279 if (src
.mSourceType
!= gfxFontFaceSrc::eSourceType_URL
) {
1282 mAllowedFontLoads
.LookupOrInsertWith(
1283 &src
, [&] { return IsFontLoadAllowed(src
); });
1289 bool FontFaceSet::IsFontLoadAllowed(const gfxFontFaceSrc
& aSrc
) {
1290 MOZ_ASSERT(aSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_URL
);
1292 if (ServoStyleSet::IsInServoTraversal()) {
1293 auto entry
= mAllowedFontLoads
.Lookup(&aSrc
);
1294 MOZ_DIAGNOSTIC_ASSERT(entry
, "Missed an update?");
1295 return entry
? *entry
: false;
1298 MOZ_ASSERT(NS_IsMainThread());
1300 if (!mUserFontSet
) {
1304 gfxFontSrcPrincipal
* gfxPrincipal
= aSrc
.mURI
->InheritsSecurityContext()
1306 : aSrc
.LoadPrincipal(*mUserFontSet
);
1308 nsIPrincipal
* principal
=
1309 gfxPrincipal
? gfxPrincipal
->NodePrincipal() : nullptr;
1311 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
= new net::LoadInfo(
1312 mDocument
->NodePrincipal(), // loading principal
1313 principal
, // triggering principal
1314 mDocument
, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
,
1315 nsIContentPolicy::TYPE_FONT
);
1317 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
1318 nsresult rv
= NS_CheckContentLoadPolicy(aSrc
.mURI
->get(), secCheckLoadInfo
,
1321 nsContentUtils::GetContentPolicy());
1323 return NS_SUCCEEDED(rv
) && NS_CP_ACCEPTED(shouldLoad
);
1326 void FontFaceSet::DispatchFontLoadViolations(
1327 nsTArray
<nsCOMPtr
<nsIRunnable
>>& aViolations
) {
1328 if (XRE_IsContentProcess()) {
1329 nsCOMPtr
<nsIEventTarget
> eventTarget
=
1330 mDocument
->EventTargetFor(TaskCategory::Other
);
1331 for (nsIRunnable
* runnable
: aViolations
) {
1332 eventTarget
->Dispatch(do_AddRef(runnable
), NS_DISPATCH_NORMAL
);
1335 for (nsIRunnable
* runnable
: aViolations
) {
1336 NS_DispatchToMainThread(do_AddRef(runnable
));
1341 nsresult
FontFaceSet::SyncLoadFontData(gfxUserFontEntry
* aFontToLoad
,
1342 const gfxFontFaceSrc
* aFontFaceSrc
,
1344 uint32_t& aBufferLength
) {
1347 gfxFontSrcPrincipal
* principal
= aFontToLoad
->GetPrincipal();
1349 nsCOMPtr
<nsIChannel
> channel
;
1350 // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
1351 // node and a principal. This is because the document where the font is
1352 // being loaded might have a different origin from the principal of the
1353 // stylesheet that initiated the font load.
1354 // Further, we only get here for data: loads, so it doesn't really matter
1355 // whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be
1356 // more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
1357 rv
= NS_NewChannelWithTriggeringPrincipal(
1358 getter_AddRefs(channel
), aFontFaceSrc
->mURI
->get(), mDocument
,
1359 principal
? principal
->NodePrincipal() : nullptr,
1360 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
,
1361 nsIContentPolicy::TYPE_FONT
);
1363 NS_ENSURE_SUCCESS(rv
, rv
);
1365 // blocking stream is OK for data URIs
1366 nsCOMPtr
<nsIInputStream
> stream
;
1367 rv
= channel
->Open(getter_AddRefs(stream
));
1368 NS_ENSURE_SUCCESS(rv
, rv
);
1370 uint64_t bufferLength64
;
1371 rv
= stream
->Available(&bufferLength64
);
1372 NS_ENSURE_SUCCESS(rv
, rv
);
1373 if (bufferLength64
== 0) {
1374 return NS_ERROR_FAILURE
;
1376 if (bufferLength64
> UINT32_MAX
) {
1377 return NS_ERROR_FILE_TOO_BIG
;
1379 aBufferLength
= static_cast<uint32_t>(bufferLength64
);
1381 // read all the decoded data
1382 aBuffer
= static_cast<uint8_t*>(malloc(sizeof(uint8_t) * aBufferLength
));
1385 return NS_ERROR_OUT_OF_MEMORY
;
1388 uint32_t numRead
, totalRead
= 0;
1389 while (NS_SUCCEEDED(
1390 rv
= stream
->Read(reinterpret_cast<char*>(aBuffer
+ totalRead
),
1391 aBufferLength
- totalRead
, &numRead
)) &&
1393 totalRead
+= numRead
;
1394 if (totalRead
> aBufferLength
) {
1395 rv
= NS_ERROR_FAILURE
;
1400 // make sure there's a mime type
1401 if (NS_SUCCEEDED(rv
)) {
1402 nsAutoCString mimeType
;
1403 rv
= channel
->GetContentType(mimeType
);
1404 aBufferLength
= totalRead
;
1407 if (NS_FAILED(rv
)) {
1417 void FontFaceSet::OnFontFaceStatusChanged(FontFace
* aFontFace
) {
1418 AssertIsMainThreadOrServoFontMetricsLocked();
1420 MOZ_ASSERT(HasAvailableFontFace(aFontFace
));
1422 mHasLoadingFontFacesIsDirty
= true;
1424 if (aFontFace
->Status() == FontFaceLoadStatus::Loading
) {
1425 CheckLoadingStarted();
1427 MOZ_ASSERT(aFontFace
->Status() == FontFaceLoadStatus::Loaded
||
1428 aFontFace
->Status() == FontFaceLoadStatus::Error
);
1429 // When a font finishes downloading, nsPresContext::UserFontSetUpdated
1430 // will be called immediately afterwards to request a reflow of the
1431 // relevant elements in the document. We want to wait until the reflow
1432 // request has been done before the FontFaceSet is marked as Loaded so
1433 // that we don't briefly set the FontFaceSet to Loaded and then Loading
1434 // again once the reflow is pending. So we go around the event loop
1435 // and call CheckLoadingFinished() after the reflow has been queued.
1436 if (!mDelayedLoadCheck
) {
1437 mDelayedLoadCheck
= true;
1438 DispatchCheckLoadingFinishedAfterDelay();
1443 void FontFaceSet::DispatchCheckLoadingFinishedAfterDelay() {
1444 AssertIsMainThreadOrServoFontMetricsLocked();
1446 if (ServoStyleSet
* set
= ServoStyleSet::Current()) {
1447 // See comments in Gecko_GetFontMetrics.
1449 // We can't just dispatch the runnable below if we're not on the main
1450 // thread, since it needs to take a strong reference to the FontFaceSet,
1451 // and being a DOM object, FontFaceSet doesn't support thread-safe
1454 PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(
1459 nsCOMPtr
<nsIRunnable
> checkTask
=
1460 NewRunnableMethod("dom::FontFaceSet::CheckLoadingFinishedAfterDelay",
1461 this, &FontFaceSet::CheckLoadingFinishedAfterDelay
);
1462 mDocument
->Dispatch(TaskCategory::Other
, checkTask
.forget());
1465 void FontFaceSet::DidRefresh() { CheckLoadingFinished(); }
1467 void FontFaceSet::CheckLoadingFinishedAfterDelay() {
1468 mDelayedLoadCheck
= false;
1469 CheckLoadingFinished();
1472 void FontFaceSet::CheckLoadingStarted() {
1473 AssertIsMainThreadOrServoFontMetricsLocked();
1475 if (!HasLoadingFontFaces()) {
1479 if (mStatus
== FontFaceSetLoadStatus::Loading
) {
1480 // We have already dispatched a loading event and replaced mReady
1481 // with a fresh, unresolved promise.
1485 mStatus
= FontFaceSetLoadStatus::Loading
;
1486 DispatchLoadingEventAndReplaceReadyPromise();
1489 void FontFaceSet::DispatchLoadingEventAndReplaceReadyPromise() {
1490 AssertIsMainThreadOrServoFontMetricsLocked();
1492 if (ServoStyleSet
* set
= ServoStyleSet::Current()) {
1493 // See comments in Gecko_GetFontMetrics.
1495 // We can't just dispatch the runnable below if we're not on the main
1496 // thread, since it needs to take a strong reference to the FontFaceSet,
1497 // and being a DOM object, FontFaceSet doesn't support thread-safe
1498 // refcounting. (Also, the Promise object creation must be done on
1499 // the main thread.)
1501 PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this));
1505 (new AsyncEventDispatcher(this, u
"loading"_ns
, CanBubble::eNo
))
1508 if (PrefEnabled()) {
1509 if (mReady
&& mReady
->State() != Promise::PromiseState::Pending
) {
1510 if (GetParentObject()) {
1512 mReady
= Promise::Create(GetParentObject(), rv
);
1516 // We may previously have been in a state where all fonts had finished
1517 // loading and we'd set mResolveLazilyCreatedReadyPromise to make sure that
1518 // if we lazily create mReady for a consumer that we resolve it before
1519 // returning it. We're now loading fonts, so we need to clear that flag.
1520 mResolveLazilyCreatedReadyPromise
= false;
1524 void FontFaceSet::UpdateHasLoadingFontFaces() {
1525 mHasLoadingFontFacesIsDirty
= false;
1526 mHasLoadingFontFaces
= false;
1527 for (size_t i
= 0; i
< mRuleFaces
.Length(); i
++) {
1528 FontFace
* f
= mRuleFaces
[i
].mFontFace
;
1529 if (f
->Status() == FontFaceLoadStatus::Loading
) {
1530 mHasLoadingFontFaces
= true;
1534 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
1535 if (mNonRuleFaces
[i
].mFontFace
->Status() == FontFaceLoadStatus::Loading
) {
1536 mHasLoadingFontFaces
= true;
1542 bool FontFaceSet::HasLoadingFontFaces() {
1543 if (mHasLoadingFontFacesIsDirty
) {
1544 UpdateHasLoadingFontFaces();
1546 return mHasLoadingFontFaces
;
1549 bool FontFaceSet::MightHavePendingFontLoads() {
1550 // Check for FontFace objects in the FontFaceSet that are still loading.
1551 if (HasLoadingFontFaces()) {
1555 // Check for pending restyles or reflows, as they might cause fonts to
1556 // load as new styles apply and text runs are rebuilt.
1557 nsPresContext
* presContext
= GetPresContext();
1558 if (presContext
&& presContext
->HasPendingRestyleOrReflow()) {
1563 // We defer resolving mReady until the document as fully loaded.
1564 if (!mDocument
->DidFireDOMContentLoaded()) {
1568 // And we also wait for any CSS style sheets to finish loading, as their
1569 // styles might cause new fonts to load.
1570 if (mDocument
->CSSLoader()->HasPendingLoads()) {
1578 void FontFaceSet::CheckLoadingFinished() {
1579 MOZ_ASSERT(NS_IsMainThread());
1581 if (mDelayedLoadCheck
) {
1582 // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
1586 if (!ReadyPromiseIsPending()) {
1587 // We've already resolved mReady (or set the flag to do that lazily) and
1588 // dispatched the loadingdone/loadingerror events.
1592 if (MightHavePendingFontLoads()) {
1593 // We're not finished loading yet.
1597 mStatus
= FontFaceSetLoadStatus::Loaded
;
1599 mReady
->MaybeResolve(this);
1601 mResolveLazilyCreatedReadyPromise
= true;
1604 // Now dispatch the loadingdone/loadingerror events.
1605 nsTArray
<OwningNonNull
<FontFace
>> loaded
;
1606 nsTArray
<OwningNonNull
<FontFace
>> failed
;
1608 for (size_t i
= 0; i
< mRuleFaces
.Length(); i
++) {
1609 if (!mRuleFaces
[i
].mLoadEventShouldFire
) {
1612 FontFace
* f
= mRuleFaces
[i
].mFontFace
;
1613 if (f
->Status() == FontFaceLoadStatus::Loaded
) {
1614 loaded
.AppendElement(*f
);
1615 mRuleFaces
[i
].mLoadEventShouldFire
= false;
1616 } else if (f
->Status() == FontFaceLoadStatus::Error
) {
1617 failed
.AppendElement(*f
);
1618 mRuleFaces
[i
].mLoadEventShouldFire
= false;
1622 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
1623 if (!mNonRuleFaces
[i
].mLoadEventShouldFire
) {
1626 FontFace
* f
= mNonRuleFaces
[i
].mFontFace
;
1627 if (f
->Status() == FontFaceLoadStatus::Loaded
) {
1628 loaded
.AppendElement(*f
);
1629 mNonRuleFaces
[i
].mLoadEventShouldFire
= false;
1630 } else if (f
->Status() == FontFaceLoadStatus::Error
) {
1631 failed
.AppendElement(*f
);
1632 mNonRuleFaces
[i
].mLoadEventShouldFire
= false;
1636 DispatchLoadingFinishedEvent(u
"loadingdone"_ns
, std::move(loaded
));
1638 if (!failed
.IsEmpty()) {
1639 DispatchLoadingFinishedEvent(u
"loadingerror"_ns
, std::move(failed
));
1643 void FontFaceSet::DispatchLoadingFinishedEvent(
1644 const nsAString
& aType
, nsTArray
<OwningNonNull
<FontFace
>>&& aFontFaces
) {
1645 FontFaceSetLoadEventInit init
;
1646 init
.mBubbles
= false;
1647 init
.mCancelable
= false;
1648 init
.mFontfaces
= std::move(aFontFaces
);
1649 RefPtr
<FontFaceSetLoadEvent
> event
=
1650 FontFaceSetLoadEvent::Constructor(this, aType
, init
);
1651 (new AsyncEventDispatcher(this, event
))->PostDOMEvent();
1654 // nsIDOMEventListener
1657 FontFaceSet::HandleEvent(Event
* aEvent
) {
1659 aEvent
->GetType(type
);
1661 if (!type
.EqualsLiteral("DOMContentLoaded")) {
1662 return NS_ERROR_FAILURE
;
1665 RemoveDOMContentLoadedListener();
1666 CheckLoadingFinished();
1672 bool FontFaceSet::PrefEnabled() {
1673 return StaticPrefs::layout_css_font_loading_api_enabled();
1676 // nsICSSLoaderObserver
1679 FontFaceSet::StyleSheetLoaded(StyleSheet
* aSheet
, bool aWasDeferred
,
1681 CheckLoadingFinished();
1685 void FontFaceSet::FlushUserFontSet() {
1687 mDocument
->FlushUserFontSet();
1691 void FontFaceSet::MarkUserFontSetDirty() {
1693 // Ensure we trigger at least a style flush, that will eventually flush the
1694 // user font set. Otherwise the font loads that that flush may cause could
1695 // never be triggered.
1696 if (PresShell
* presShell
= mDocument
->GetPresShell()) {
1697 presShell
->EnsureStyleFlush();
1699 mDocument
->MarkUserFontSetDirty();
1703 nsPresContext
* FontFaceSet::GetPresContext() {
1708 return mDocument
->GetPresContext();
1711 void FontFaceSet::RefreshStandardFontLoadPrincipal() {
1712 MOZ_ASSERT(NS_IsMainThread());
1713 mStandardFontLoadPrincipal
= new gfxFontSrcPrincipal(
1714 mDocument
->NodePrincipal(), mDocument
->PartitionedPrincipal());
1715 mAllowedFontLoads
.Clear();
1717 mUserFontSet
->IncrementGeneration(false);
1721 void FontFaceSet::CopyNonRuleFacesTo(FontFaceSet
* aFontFaceSet
) const {
1722 for (const FontFaceRecord
& rec
: mNonRuleFaces
) {
1724 RefPtr
<FontFace
> f
= rec
.mFontFace
;
1725 aFontFaceSet
->Add(*f
, rv
);
1726 MOZ_ASSERT(!rv
.Failed());
1730 // -- FontFaceSet::UserFontSet ------------------------------------------------
1733 bool FontFaceSet::UserFontSet::IsFontLoadAllowed(const gfxFontFaceSrc
& aSrc
) {
1734 return mFontFaceSet
&& mFontFaceSet
->IsFontLoadAllowed(aSrc
);
1738 void FontFaceSet::UserFontSet::DispatchFontLoadViolations(
1739 nsTArray
<nsCOMPtr
<nsIRunnable
>>& aViolations
) {
1741 mFontFaceSet
->DispatchFontLoadViolations(aViolations
);
1746 nsresult
FontFaceSet::UserFontSet::StartLoad(
1747 gfxUserFontEntry
* aUserFontEntry
, const gfxFontFaceSrc
* aFontFaceSrc
) {
1748 if (!mFontFaceSet
) {
1749 return NS_ERROR_FAILURE
;
1751 return mFontFaceSet
->StartLoad(aUserFontEntry
, aFontFaceSrc
);
1754 void FontFaceSet::UserFontSet::RecordFontLoadDone(uint32_t aFontSize
,
1755 TimeStamp aDoneTime
) {
1757 mDownloadSize
+= aFontSize
;
1758 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE
, aFontSize
/ 1024);
1760 if (!mFontFaceSet
) {
1764 TimeStamp navStart
= mFontFaceSet
->GetNavigationStartTimeStamp();
1766 if (navStart
!= zero
) {
1767 Telemetry::AccumulateTimeDelta(Telemetry::WEBFONT_DOWNLOAD_TIME_AFTER_START
,
1768 navStart
, aDoneTime
);
1773 nsresult
FontFaceSet::UserFontSet::LogMessage(gfxUserFontEntry
* aUserFontEntry
,
1774 const char* aMessage
,
1777 if (!mFontFaceSet
) {
1778 return NS_ERROR_FAILURE
;
1780 return mFontFaceSet
->LogMessage(aUserFontEntry
, aMessage
, aFlags
, aStatus
);
1784 nsresult
FontFaceSet::UserFontSet::SyncLoadFontData(
1785 gfxUserFontEntry
* aFontToLoad
, const gfxFontFaceSrc
* aFontFaceSrc
,
1786 uint8_t*& aBuffer
, uint32_t& aBufferLength
) {
1787 if (!mFontFaceSet
) {
1788 return NS_ERROR_FAILURE
;
1790 return mFontFaceSet
->SyncLoadFontData(aFontToLoad
, aFontFaceSrc
, aBuffer
,
1795 bool FontFaceSet::UserFontSet::GetPrivateBrowsing() {
1796 return mFontFaceSet
&& mFontFaceSet
->mPrivateBrowsing
;
1800 void FontFaceSet::UserFontSet::DoRebuildUserFontSet() {
1801 if (!mFontFaceSet
) {
1804 mFontFaceSet
->MarkUserFontSetDirty();
1808 already_AddRefed
<gfxUserFontEntry
>
1809 FontFaceSet::UserFontSet::CreateUserFontEntry(
1810 const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
, WeightRange aWeight
,
1811 StretchRange aStretch
, SlantStyleRange aStyle
,
1812 const nsTArray
<gfxFontFeature
>& aFeatureSettings
,
1813 const nsTArray
<gfxFontVariation
>& aVariationSettings
,
1814 uint32_t aLanguageOverride
, gfxCharacterMap
* aUnicodeRanges
,
1815 StyleFontDisplay aFontDisplay
, RangeFlags aRangeFlags
) {
1816 RefPtr
<gfxUserFontEntry
> entry
= new FontFace::Entry(
1817 this, aFontFaceSrcList
, aWeight
, aStretch
, aStyle
, aFeatureSettings
,
1818 aVariationSettings
, aLanguageOverride
, aUnicodeRanges
, aFontDisplay
,
1820 return entry
.forget();