1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
8 #include "gfxUserFontSet.h"
9 #include "gfxPlatform.h"
10 #include "gfxFontConstants.h"
11 #include "mozilla/Atomics.h"
12 #include "mozilla/FontPropertyTypes.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/ProfilerLabels.h"
15 #include "mozilla/Services.h"
16 #include "mozilla/StaticPrefs_gfx.h"
17 #include "mozilla/Telemetry.h"
18 #include "mozilla/gfx/2D.h"
19 #include "gfxPlatformFontList.h"
20 #include "mozilla/PostTraversalTask.h"
21 #include "gfxOTSUtils.h"
22 #include "nsIFontLoadCompleteCallback.h"
23 #include "nsProxyRelease.h"
24 #include "nsContentUtils.h"
25 #include "nsTHashSet.h"
27 using namespace mozilla
;
29 mozilla::LogModule
* gfxUserFontSet::GetUserFontsLog() {
30 static LazyLogModule
sLog("userfonts");
35 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
36 #define LOG_ENABLED() \
37 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug)
39 static Atomic
<uint64_t> sFontSetGeneration(0);
41 gfxUserFontEntry::gfxUserFontEntry(nsTArray
<gfxFontFaceSrc
>&& aFontFaceSrcList
,
42 gfxUserFontAttributes
&& aAttr
)
43 : gfxFontEntry("userfont"_ns
),
44 mUserFontLoadState(STATUS_NOT_LOADED
),
45 mFontDataLoadingState(NOT_LOADING
),
46 mSeenLocalSource(false),
47 mUnsupportedFormat(false),
48 mFontDisplay(aAttr
.mFontDisplay
),
50 mIsUserFontContainer
= true;
51 mSrcList
= std::move(aFontFaceSrcList
);
53 mWeightRange
= aAttr
.mWeight
;
54 mStretchRange
= aAttr
.mStretch
;
55 mStyleRange
= aAttr
.mStyle
;
56 mFeatureSettings
= std::move(aAttr
.mFeatureSettings
);
57 mVariationSettings
= std::move(aAttr
.mVariationSettings
);
58 mLanguageOverride
= aAttr
.mLanguageOverride
;
59 SetUnicodeRangeMap(std::move(aAttr
.mUnicodeRanges
));
60 mRangeFlags
= aAttr
.mRangeFlags
;
61 mAscentOverride
= aAttr
.mAscentOverride
;
62 mDescentOverride
= aAttr
.mDescentOverride
;
63 mLineGapOverride
= aAttr
.mLineGapOverride
;
64 mSizeAdjust
= aAttr
.mSizeAdjust
;
65 mFamilyName
= aAttr
.mFamilyName
;
68 void gfxUserFontEntry::UpdateAttributes(gfxUserFontAttributes
&& aAttr
) {
69 MOZ_ASSERT(NS_IsMainThread());
71 // Remove the entry from the user font cache, if present there, as the cache
72 // key may no longer be correct with the new attributes.
73 gfxUserFontSet::UserFontCache::ForgetFont(this);
75 mFontDisplay
= aAttr
.mFontDisplay
;
76 mWeightRange
= aAttr
.mWeight
;
77 mStretchRange
= aAttr
.mStretch
;
78 mStyleRange
= aAttr
.mStyle
;
79 mFeatureSettings
= std::move(aAttr
.mFeatureSettings
);
80 mVariationSettings
= std::move(aAttr
.mVariationSettings
);
81 mLanguageOverride
= aAttr
.mLanguageOverride
;
82 SetUnicodeRangeMap(std::move(aAttr
.mUnicodeRanges
));
83 mRangeFlags
= aAttr
.mRangeFlags
;
84 mAscentOverride
= aAttr
.mAscentOverride
;
85 mDescentOverride
= aAttr
.mDescentOverride
;
86 mLineGapOverride
= aAttr
.mLineGapOverride
;
87 mSizeAdjust
= aAttr
.mSizeAdjust
;
90 gfxUserFontEntry::~gfxUserFontEntry() {
91 // Assert that we don't drop any gfxUserFontEntry objects during a Servo
92 // traversal, since PostTraversalTask objects can hold raw pointers to
93 // gfxUserFontEntry objects.
94 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
97 bool gfxUserFontEntry::Matches(const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
98 const gfxUserFontAttributes
& aAttr
) {
99 return mWeightRange
== aAttr
.mWeight
&& mStretchRange
== aAttr
.mStretch
&&
100 mStyleRange
== aAttr
.mStyle
&&
101 mFeatureSettings
== aAttr
.mFeatureSettings
&&
102 mVariationSettings
== aAttr
.mVariationSettings
&&
103 mLanguageOverride
== aAttr
.mLanguageOverride
&&
104 mSrcList
== aFontFaceSrcList
&& mFontDisplay
== aAttr
.mFontDisplay
&&
105 mRangeFlags
== aAttr
.mRangeFlags
&&
106 mAscentOverride
== aAttr
.mAscentOverride
&&
107 mDescentOverride
== aAttr
.mDescentOverride
&&
108 mLineGapOverride
== aAttr
.mLineGapOverride
&&
109 mSizeAdjust
== aAttr
.mSizeAdjust
&&
110 ((!aAttr
.mUnicodeRanges
&& !mCharacterMap
) ||
111 (aAttr
.mUnicodeRanges
&& mCharacterMap
&&
112 GetCharacterMap()->Equals(aAttr
.mUnicodeRanges
)));
115 gfxFont
* gfxUserFontEntry::CreateFontInstance(const gfxFontStyle
* aFontStyle
) {
116 MOZ_ASSERT_UNREACHABLE(
117 "should only be creating a gfxFont"
118 " with an actual platform font entry");
120 // userfont entry is a container, can't create font from the container
124 class MOZ_STACK_CLASS gfxOTSMessageContext
: public gfxOTSContext
{
126 virtual ~gfxOTSMessageContext() {
127 MOZ_ASSERT(mMessages
.IsEmpty(), "should have called TakeMessages");
130 virtual void Message(int level
, const char* format
,
131 ...) MSGFUNC_FMT_ATTR override
{
134 // Special-case glyph bounding box warnings: collect all bad glyph IDs,
135 // so we can issue a single message at the end.
136 if (level
> 0 && strstr(format
, "bbox was incorrect")) {
137 // Extract the glyph ID from the message: it follows the last space in
138 // the message string.
139 const char* lastSpace
= strrchr(format
, ' ');
141 int gid
= atoi(lastSpace
+ 1);
142 mBadBBoxGlyphs
.AppendElement(gid
);
147 va_start(va
, format
);
150 msg
.AppendVprintf(format
, va
);
155 // For warnings (rather than errors that cause the font to fail),
156 // we only report the first instance of any given message.
157 if (!mWarningsIssued
.EnsureInserted(msg
)) {
162 mMessages
.AppendElement(gfxUserFontEntry::OTSMessage
{msg
, level
});
165 bool Process(ots::OTSStream
* aOutput
, const uint8_t* aInput
, size_t aLength
,
166 nsTArray
<gfxUserFontEntry::OTSMessage
>& aMessages
) {
167 bool ok
= ots::OTSContext::Process(aOutput
, aInput
, aLength
);
168 aMessages
= TakeMessages();
172 nsTArray
<gfxUserFontEntry::OTSMessage
>&& TakeMessages() {
173 if (!mBadBBoxGlyphs
.IsEmpty()) {
174 nsAutoCString
msg("Glyph bbox was incorrect (glyph ids");
175 for (const auto gid
: mBadBBoxGlyphs
) {
180 mMessages
.AppendElement(gfxUserFontEntry::OTSMessage
{msg
, 1});
181 mBadBBoxGlyphs
.Clear();
183 return std::move(mMessages
);
187 nsTHashSet
<nsCString
> mWarningsIssued
;
188 nsTArray
<gfxUserFontEntry::OTSMessage
> mMessages
;
189 nsTArray
<uint16_t> mBadBBoxGlyphs
;
192 // Call the OTS library to sanitize an sfnt before attempting to use it.
193 // Returns a newly-allocated block, or nullptr in case of fatal errors.
194 const uint8_t* gfxUserFontEntry::SanitizeOpenTypeData(
195 const uint8_t* aData
, uint32_t aLength
, uint32_t& aSanitaryLength
,
196 gfxUserFontType
& aFontType
, nsTArray
<OTSMessage
>& aMessages
) {
197 aFontType
= gfxFontUtils::DetermineFontDataType(aData
, aLength
);
198 Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE
, uint32_t(aFontType
));
200 size_t lengthHint
= gfxOTSContext::GuessSanitizedFontSize(aLength
, aFontType
);
206 gfxOTSExpandingMemoryStream
<gfxOTSMozAlloc
> output(lengthHint
);
208 gfxOTSMessageContext otsContext
;
209 if (!otsContext
.Process(&output
, aData
, aLength
, aMessages
)) {
210 // Failed to decode/sanitize the font, so discard it.
215 aSanitaryLength
= output
.Tell();
216 return static_cast<const uint8_t*>(output
.forget());
219 void gfxUserFontEntry::StoreUserFontData(gfxFontEntry
* aFontEntry
,
220 uint32_t aSrcIndex
, bool aPrivate
,
221 const nsACString
& aOriginalName
,
222 FallibleTArray
<uint8_t>* aMetadata
,
223 uint32_t aMetaOrigLen
,
224 uint8_t aCompression
) {
225 if (!aFontEntry
->mUserFontData
) {
226 aFontEntry
->mUserFontData
= MakeUnique
<gfxUserFontData
>();
228 gfxUserFontData
* userFontData
= aFontEntry
->mUserFontData
.get();
229 userFontData
->mSrcIndex
= aSrcIndex
;
230 const gfxFontFaceSrc
& src
= mSrcList
[aSrcIndex
];
231 switch (src
.mSourceType
) {
232 case gfxFontFaceSrc::eSourceType_Local
:
233 userFontData
->mLocalName
= src
.mLocalName
;
235 case gfxFontFaceSrc::eSourceType_URL
:
236 userFontData
->mURI
= src
.mURI
;
237 userFontData
->mPrincipal
= mPrincipal
;
239 case gfxFontFaceSrc::eSourceType_Buffer
:
240 userFontData
->mIsBuffer
= true;
243 userFontData
->mPrivate
= aPrivate
;
244 userFontData
->mTechFlags
= src
.mTechFlags
;
245 userFontData
->mFormatHint
= src
.mFormatHint
;
246 userFontData
->mRealName
= aOriginalName
;
248 userFontData
->mMetadata
= std::move(*aMetadata
);
249 userFontData
->mMetaOrigLen
= aMetaOrigLen
;
250 userFontData
->mCompression
= aCompression
;
254 size_t gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
255 return aMallocSizeOf(this) +
256 mMetadata
.ShallowSizeOfExcludingThis(aMallocSizeOf
) +
257 mLocalName
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
) +
258 mRealName
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
259 // Not counting mURI and mPrincipal, as those will be shared.
263 gfxUserFontFamily::~gfxUserFontFamily() {
264 // Should not be dropped by stylo
265 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
268 already_AddRefed
<gfxFontSrcPrincipal
> gfxFontFaceSrc::LoadPrincipal(
269 const gfxUserFontSet
& aFontSet
) const {
270 MOZ_ASSERT(mSourceType
== eSourceType_URL
);
271 if (mUseOriginPrincipal
) {
272 MOZ_ASSERT(mOriginPrincipal
);
273 return RefPtr
{mOriginPrincipal
}.forget();
275 return aFontSet
.GetStandardFontLoadPrincipal();
278 void gfxUserFontEntry::GetFamilyNameAndURIForLogging(uint32_t aSrcIndex
,
279 nsACString
& aFamilyName
,
281 aFamilyName
= mFamilyName
;
284 if (aSrcIndex
>= mSrcList
.Length()) {
285 aURI
.AppendLiteral("(end of source list)");
287 if (mSrcList
[aSrcIndex
].mURI
) {
288 mSrcList
[aSrcIndex
].mURI
->GetSpec(aURI
);
289 // If the source URI was very long, elide the middle of it.
290 // In principle, the byte-oriented chopping here could leave us
291 // with partial UTF-8 characters at the point where we cut it,
292 // but it really doesn't matter as this is just for logging.
293 const uint32_t kMaxURILengthForLogging
= 256;
294 // UTF-8 ellipsis, with spaces to allow additional wrap opportunities
295 // in the resulting log message
296 const char kEllipsis
[] = {' ', '\xE2', '\x80', '\xA6', ' '};
297 if (aURI
.Length() > kMaxURILengthForLogging
) {
298 aURI
.Replace(kMaxURILengthForLogging
/ 2,
299 aURI
.Length() - kMaxURILengthForLogging
, kEllipsis
,
300 ArrayLength(kEllipsis
));
303 aURI
.AppendLiteral("(invalid URI)");
309 AutoSwap_PRUint32 signature
;
310 AutoSwap_PRUint32 flavor
;
311 AutoSwap_PRUint32 length
;
312 AutoSwap_PRUint16 numTables
;
313 AutoSwap_PRUint16 reserved
;
314 AutoSwap_PRUint32 totalSfntSize
;
315 AutoSwap_PRUint16 majorVersion
;
316 AutoSwap_PRUint16 minorVersion
;
317 AutoSwap_PRUint32 metaOffset
;
318 AutoSwap_PRUint32 metaCompLen
;
319 AutoSwap_PRUint32 metaOrigLen
;
320 AutoSwap_PRUint32 privOffset
;
321 AutoSwap_PRUint32 privLen
;
325 AutoSwap_PRUint32 signature
;
326 AutoSwap_PRUint32 flavor
;
327 AutoSwap_PRUint32 length
;
328 AutoSwap_PRUint16 numTables
;
329 AutoSwap_PRUint16 reserved
;
330 AutoSwap_PRUint32 totalSfntSize
;
331 AutoSwap_PRUint32 totalCompressedSize
;
332 AutoSwap_PRUint16 majorVersion
;
333 AutoSwap_PRUint16 minorVersion
;
334 AutoSwap_PRUint32 metaOffset
;
335 AutoSwap_PRUint32 metaCompLen
;
336 AutoSwap_PRUint32 metaOrigLen
;
337 AutoSwap_PRUint32 privOffset
;
338 AutoSwap_PRUint32 privLen
;
341 template <typename HeaderT
>
342 void CopyWOFFMetadata(const uint8_t* aFontData
, uint32_t aLength
,
343 FallibleTArray
<uint8_t>* aMetadata
,
344 uint32_t* aMetaOrigLen
) {
345 // This function may be called with arbitrary, unvalidated "font" data
346 // from @font-face, so it needs to be careful to bounds-check, etc.,
347 // before trying to read anything.
348 // This just saves a copy of the compressed data block; it does NOT check
349 // that the block can be successfully decompressed, or that it contains
350 // well-formed/valid XML metadata.
351 if (aLength
< sizeof(HeaderT
)) {
354 const HeaderT
* woff
= reinterpret_cast<const HeaderT
*>(aFontData
);
355 uint32_t metaOffset
= woff
->metaOffset
;
356 uint32_t metaCompLen
= woff
->metaCompLen
;
357 if (!metaOffset
|| !metaCompLen
|| !woff
->metaOrigLen
) {
360 if (metaOffset
>= aLength
|| metaCompLen
> aLength
- metaOffset
) {
363 if (!aMetadata
->SetLength(woff
->metaCompLen
, fallible
)) {
366 memcpy(aMetadata
->Elements(), aFontData
+ metaOffset
, metaCompLen
);
367 *aMetaOrigLen
= woff
->metaOrigLen
;
370 void gfxUserFontEntry::LoadNextSrc() {
371 NS_ASSERTION(mCurrentSrcIndex
< mSrcList
.Length(),
372 "already at the end of the src list for user font");
373 NS_ASSERTION((mUserFontLoadState
== STATUS_NOT_LOADED
||
374 mUserFontLoadState
== STATUS_LOAD_PENDING
||
375 mUserFontLoadState
== STATUS_LOADING
) &&
376 mFontDataLoadingState
< LOADING_FAILED
,
377 "attempting to load a font that has either completed or failed");
379 if (mUserFontLoadState
== STATUS_NOT_LOADED
) {
380 SetLoadState(STATUS_LOADING
);
381 mFontDataLoadingState
= LOADING_STARTED
;
382 mUnsupportedFormat
= false;
384 // we were already loading; move to the next source,
385 // but don't reset state - if we've already timed out,
386 // that counts against the new download
390 DoLoadNextSrc(false);
393 void gfxUserFontEntry::ContinueLoad() {
394 MOZ_ASSERT(mUserFontLoadState
== STATUS_LOAD_PENDING
);
395 MOZ_ASSERT(mSrcList
[mCurrentSrcIndex
].mSourceType
==
396 gfxFontFaceSrc::eSourceType_URL
);
398 SetLoadState(STATUS_LOADING
);
399 DoLoadNextSrc(/* aIsContinue = */ true);
400 if (LoadState() != STATUS_LOADING
) {
401 MOZ_ASSERT(mUserFontLoadState
!= STATUS_LOAD_PENDING
,
402 "Not in parallel traversal, shouldn't get LOAD_PENDING again");
403 // Loading is synchronously finished (loaded from cache or failed). We
404 // need to increment the generation so that we flush the style data to
405 // use the new loaded font face.
406 // Without parallel traversal, we would simply get the right font data
407 // after the first call to DoLoadNextSrc() in this case, so we don't need
408 // to touch the generation to trigger another restyle.
409 // XXX We may want to return synchronously in parallel traversal in those
410 // cases as well if possible, so that we don't have an additional restyle.
411 // That doesn't work currently because Document::GetDocShell (called from
412 // FontFaceSet::CheckFontLoad) dereferences a weak pointer, which is not
413 // allowed in parallel traversal.
414 IncrementGeneration();
418 static bool IgnorePrincipal(gfxFontSrcURI
* aURI
) {
419 return aURI
->InheritsSecurityContext();
422 void gfxUserFontEntry::DoLoadNextSrc(bool aIsContinue
) {
423 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
424 if (NS_WARN_IF(!fontSet
)) {
425 LOG(("userfonts (%p) failed expired font set for (%s)\n", fontSet
.get(),
427 mFontDataLoadingState
= LOADING_FAILED
;
428 SetLoadState(STATUS_FAILED
);
432 uint32_t numSrc
= mSrcList
.Length();
434 // load each src entry in turn, until a local face is found
435 // or a download begins successfully
436 while (mCurrentSrcIndex
< numSrc
) {
437 gfxFontFaceSrc
& currSrc
= mSrcList
[mCurrentSrcIndex
];
439 // src local ==> lookup and load immediately
441 if (currSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_Local
) {
442 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
443 pfl
->AddUserFontSet(fontSet
);
444 // Don't look up local fonts if the font whitelist is being used.
445 gfxFontEntry
* fe
= nullptr;
446 if (!pfl
->IsFontFamilyWhitelistActive()) {
447 fe
= gfxPlatform::GetPlatform()->LookupLocalFont(
448 fontSet
->GetPresContext(), currSrc
.mLocalName
, Weight(), Stretch(),
450 // Note that we've attempted a local lookup, even if it failed,
451 // as this means we are dependent on any updates to the font list.
452 mSeenLocalSource
= true;
453 nsTArray
<RefPtr
<gfxUserFontSet
>> fontSets
;
454 GetUserFontSets(fontSets
);
455 for (gfxUserFontSet
* fontSet
: fontSets
) {
456 // We need to note on each gfxUserFontSet that contains the user
457 // font entry that we used a local() rule.
458 fontSet
->SetLocalRulesUsed();
462 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
463 fontSet
.get(), mCurrentSrcIndex
, currSrc
.mLocalName
.get(),
464 mFamilyName
.get(), uint32_t(fontSet
->mGeneration
)));
465 fe
->mFeatureSettings
.AppendElements(mFeatureSettings
);
466 fe
->mVariationSettings
.AppendElements(mVariationSettings
);
467 fe
->mLanguageOverride
= mLanguageOverride
;
468 fe
->mFamilyName
= mFamilyName
;
469 fe
->mRangeFlags
= mRangeFlags
;
470 fe
->mAscentOverride
= mAscentOverride
;
471 fe
->mDescentOverride
= mDescentOverride
;
472 fe
->mLineGapOverride
= mLineGapOverride
;
473 fe
->mSizeAdjust
= mSizeAdjust
;
474 // For src:local(), we don't care whether the request is from
475 // a private window as there's no issue of caching resources;
476 // local fonts are just available all the time.
477 StoreUserFontData(fe
, mCurrentSrcIndex
, false, nsCString(), nullptr, 0,
478 gfxUserFontData::kUnknownCompression
);
479 mPlatformFontEntry
= fe
;
480 SetLoadState(STATUS_LOADED
);
481 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE
,
482 currSrc
.mSourceType
+ 1);
485 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
486 fontSet
.get(), mCurrentSrcIndex
, currSrc
.mLocalName
.get(),
490 // src url ==> start the load process
491 else if (currSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_URL
) {
492 if (gfxPlatform::GetPlatform()->IsFontFormatSupported(
493 currSrc
.mFormatHint
, currSrc
.mTechFlags
)) {
494 if (ServoStyleSet
* set
= gfxFontUtils::CurrentServoStyleSet()) {
495 // Only support style worker threads synchronously getting
496 // entries from the font cache when it's not a data: URI
497 // @font-face that came from UA or user sheets, since we
498 // were not able to call IsFontLoadAllowed ahead of time
499 // for these entries.
500 if (currSrc
.mUseOriginPrincipal
&& IgnorePrincipal(currSrc
.mURI
)) {
501 set
->AppendTask(PostTraversalTask::LoadFontEntry(this));
502 SetLoadState(STATUS_LOAD_PENDING
);
507 // see if we have an existing entry for this source
509 gfxUserFontSet::UserFontCache::GetFont(currSrc
, *this);
511 mPlatformFontEntry
= fe
;
512 SetLoadState(STATUS_LOADED
);
514 ("userfonts (%p) [src %d] "
515 "loaded uri from cache: (%s) for (%s)\n",
516 fontSet
.get(), mCurrentSrcIndex
,
517 currSrc
.mURI
->GetSpecOrDefault().get(), mFamilyName
.get()));
521 if (ServoStyleSet
* set
= gfxFontUtils::CurrentServoStyleSet()) {
522 // If we need to start a font load and we're on a style
523 // worker thread, we have to defer it.
524 set
->AppendTask(PostTraversalTask::LoadFontEntry(this));
525 SetLoadState(STATUS_LOAD_PENDING
);
529 // record the principal we should use for the load for use when
530 // creating a channel and when caching the loaded entry.
531 mPrincipal
= currSrc
.LoadPrincipal(*fontSet
);
533 const bool loadDoesntSpin
=
534 !aIsContinue
&& currSrc
.mURI
->SyncLoadIsOK();
535 if (loadDoesntSpin
) {
536 uint8_t* buffer
= nullptr;
537 uint32_t bufferLength
= 0;
539 // sync load font immediately
541 fontSet
->SyncLoadFontData(this, &currSrc
, buffer
, bufferLength
);
543 if (NS_SUCCEEDED(rv
) &&
544 LoadPlatformFontSync(mCurrentSrcIndex
, buffer
, bufferLength
)) {
545 SetLoadState(STATUS_LOADED
);
546 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE
,
547 currSrc
.mSourceType
+ 1);
550 fontSet
->LogMessage(this, mCurrentSrcIndex
, "font load failed",
551 nsIScriptError::errorFlag
, rv
);
552 } else if (!aIsContinue
) {
553 RefPtr
<nsIRunnable
> runnable
= NS_NewRunnableFunction(
554 "gfxUserFontSet::AsyncContinueLoad",
555 [loader
= RefPtr
{this}] { loader
->ContinueLoad(); });
556 SetLoadState(STATUS_LOAD_PENDING
);
557 // We don't want to trigger the channel open at random points in
558 // time, because it can run privileged JS.
559 if (!nsContentUtils::IsSafeToRunScript()) {
560 // There's a script-blocker on the stack. We know the sooner point
561 // where we can trigger the load.
562 nsContentUtils::AddScriptRunner(runnable
.forget());
564 // We dispatch with a rather high priority, since somebody actually
565 // cares about this font.
566 NS_DispatchToCurrentThreadQueue(runnable
.forget(),
567 EventQueuePriority::MediumHigh
);
571 // Actually start the async load.
572 nsresult rv
= fontSet
->StartLoad(this, mCurrentSrcIndex
);
573 if (NS_SUCCEEDED(rv
)) {
574 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
575 fontSet
.get(), mCurrentSrcIndex
,
576 currSrc
.mURI
->GetSpecOrDefault().get(), mFamilyName
.get()));
579 fontSet
->LogMessage(this, mCurrentSrcIndex
,
580 "failed to start download",
581 nsIScriptError::errorFlag
, rv
);
584 // We don't log a warning to the web console yet,
585 // as another source may load successfully
586 mUnsupportedFormat
= true;
589 // FontFace buffer ==> load immediately
590 MOZ_ASSERT(currSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_Buffer
);
592 uint8_t* buffer
= nullptr;
593 uint32_t bufferLength
= 0;
595 // sync load font immediately
596 currSrc
.mBuffer
->TakeBuffer(buffer
, bufferLength
);
598 LoadPlatformFontSync(mCurrentSrcIndex
, buffer
, bufferLength
)) {
599 // LoadPlatformFontSync takes ownership of the buffer, so no need
601 SetLoadState(STATUS_LOADED
);
602 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE
,
603 currSrc
.mSourceType
+ 1);
606 fontSet
->LogMessage(this, mCurrentSrcIndex
, "font load failed",
607 nsIScriptError::errorFlag
);
613 if (mUnsupportedFormat
) {
614 fontSet
->LogMessage(this, mCurrentSrcIndex
, "no supported format found",
615 nsIScriptError::warningFlag
);
618 // all src's failed; mark this entry as unusable (so fallback will occur)
619 LOG(("userfonts (%p) failed all src for (%s)\n", fontSet
.get(),
621 mFontDataLoadingState
= LOADING_FAILED
;
622 SetLoadState(STATUS_FAILED
);
625 void gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState
) {
626 mUserFontLoadState
= aLoadState
;
629 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc
)
631 bool gfxUserFontEntry::LoadPlatformFontSync(uint32_t aSrcIndex
,
632 const uint8_t* aFontData
,
634 AUTO_PROFILER_LABEL("gfxUserFontEntry::LoadPlatformFontSync", OTHER
);
635 NS_ASSERTION((mUserFontLoadState
== STATUS_NOT_LOADED
||
636 mUserFontLoadState
== STATUS_LOAD_PENDING
||
637 mUserFontLoadState
== STATUS_LOADING
) &&
638 mFontDataLoadingState
< LOADING_FAILED
,
639 "attempting to load a font that has either completed or failed");
641 // Unwrap/decompress/sanitize or otherwise munge the downloaded data
642 // to make a usable sfnt structure.
644 // Call the OTS sanitizer; this will also decode WOFF to sfnt
645 // if necessary. The original data in aFontData is left unchanged.
646 uint32_t sanitaryLen
;
647 gfxUserFontType fontType
;
648 nsTArray
<OTSMessage
> messages
;
649 const uint8_t* sanitaryData
=
650 SanitizeOpenTypeData(aFontData
, aLength
, sanitaryLen
, fontType
, messages
);
652 return LoadPlatformFont(aSrcIndex
, aFontData
, aLength
, fontType
, sanitaryData
,
653 sanitaryLen
, std::move(messages
));
656 void gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread(
657 uint32_t aSrcIndex
, const uint8_t* aFontData
, uint32_t aLength
,
658 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
> aCallback
) {
659 MOZ_ASSERT(!NS_IsMainThread());
661 uint32_t sanitaryLen
;
662 gfxUserFontType fontType
;
663 nsTArray
<OTSMessage
> messages
;
664 const uint8_t* sanitaryData
=
665 SanitizeOpenTypeData(aFontData
, aLength
, sanitaryLen
, fontType
, messages
);
667 nsCOMPtr
<nsIRunnable
> event
=
668 NewRunnableMethod
<uint32_t, const uint8_t*, uint32_t, gfxUserFontType
,
669 const uint8_t*, uint32_t, nsTArray
<OTSMessage
>&&,
670 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
>>(
671 "gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread", this,
672 &gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread
, aSrcIndex
,
673 aFontData
, aLength
, fontType
, sanitaryData
, sanitaryLen
,
674 std::move(messages
), aCallback
);
675 NS_DispatchToMainThread(event
.forget());
678 bool gfxUserFontEntry::LoadPlatformFont(uint32_t aSrcIndex
,
679 const uint8_t* aOriginalFontData
,
680 uint32_t aOriginalLength
,
681 gfxUserFontType aFontType
,
682 const uint8_t* aSanitizedFontData
,
683 uint32_t aSanitizedLength
,
684 nsTArray
<OTSMessage
>&& aMessages
) {
685 MOZ_ASSERT(NS_IsMainThread());
686 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
687 if (NS_WARN_IF(!fontSet
)) {
688 free((void*)aOriginalFontData
);
689 free((void*)aSanitizedFontData
);
693 for (const auto& msg
: aMessages
) {
694 fontSet
->LogMessage(this, aSrcIndex
, msg
.mMessage
.get(),
695 msg
.mLevel
> 0 ? nsIScriptError::warningFlag
696 : nsIScriptError::errorFlag
);
699 if (!aSanitizedFontData
) {
700 fontSet
->LogMessage(this, aSrcIndex
, "rejected by sanitizer");
702 // Check whether aSanitizedFontData is a known OpenType format; it might be
703 // a TrueType Collection, which OTS would accept but we don't yet
704 // know how to handle. If so, discard.
705 if (gfxFontUtils::DetermineFontDataType(
706 aSanitizedFontData
, aSanitizedLength
) != GFX_USERFONT_OPENTYPE
) {
707 fontSet
->LogMessage(this, aSrcIndex
, "not a supported OpenType format");
708 free((void*)aSanitizedFontData
);
709 aSanitizedFontData
= nullptr;
713 // Because platform font activation code may replace the name table
714 // in the font with a synthetic one, we save the original name so that
715 // it can be reported via the InspectorUtils API.
716 nsAutoCString originalFullName
;
718 gfxFontEntry
* fe
= nullptr;
719 uint32_t fontCompressionRatio
= 0;
720 size_t computedSize
= 0;
722 if (aSanitizedFontData
) {
723 if (aSanitizedLength
) {
724 fontCompressionRatio
=
725 uint32_t(100.0 * aOriginalLength
/ aSanitizedLength
+ 0.5);
726 if (aFontType
== GFX_USERFONT_WOFF
|| aFontType
== GFX_USERFONT_WOFF2
) {
727 Telemetry::Accumulate(aFontType
== GFX_USERFONT_WOFF
728 ? Telemetry::WEBFONT_COMPRESSION_WOFF
729 : Telemetry::WEBFONT_COMPRESSION_WOFF2
,
730 fontCompressionRatio
);
734 // The sanitizer ensures that we have a valid sfnt and a usable
735 // name table, so this should never fail unless we're out of
736 // memory, and GetFullNameFromSFNT is not directly exposed to
737 // arbitrary/malicious data from the web.
738 gfxFontUtils::GetFullNameFromSFNT(aSanitizedFontData
, aSanitizedLength
,
741 // Record size for memory reporting purposes. We measure this now
742 // because by the time we potentially want to collect reports, this
743 // data block may have been handed off to opaque OS font APIs that
744 // don't allow us to retrieve or measure it directly.
745 // The *OnAlloc function will also tell DMD about this block, as the
746 // OS font code may hold on to it for an extended period.
747 computedSize
= UserFontMallocSizeOfOnAlloc(aSanitizedFontData
);
749 // Here ownership of aSanitizedFontData is passed to the platform,
750 // which will delete it when no longer required
751 fe
= gfxPlatform::GetPlatform()->MakePlatformFont(
752 mName
, Weight(), Stretch(), SlantStyle(), aSanitizedFontData
,
755 fontSet
->LogMessage(this, aSrcIndex
, "not usable by platform");
760 fe
->mComputedSizeOfUserFont
= computedSize
;
762 // Save a copy of the metadata block (if present) for InspectorUtils
763 // to use if required. Ownership of the metadata block will be passed
764 // to the gfxUserFontData record below.
765 FallibleTArray
<uint8_t> metadata
;
766 uint32_t metaOrigLen
= 0;
767 uint8_t compression
= gfxUserFontData::kUnknownCompression
;
768 if (aFontType
== GFX_USERFONT_WOFF
) {
769 CopyWOFFMetadata
<WOFFHeader
>(aOriginalFontData
, aOriginalLength
,
770 &metadata
, &metaOrigLen
);
771 compression
= gfxUserFontData::kZlibCompression
;
772 } else if (aFontType
== GFX_USERFONT_WOFF2
) {
773 CopyWOFFMetadata
<WOFF2Header
>(aOriginalFontData
, aOriginalLength
,
774 &metadata
, &metaOrigLen
);
775 compression
= gfxUserFontData::kBrotliCompression
;
778 // copy OpenType feature/language settings from the userfont entry to the
779 // newly-created font entry
780 fe
->mFeatureSettings
.AppendElements(mFeatureSettings
);
781 fe
->mVariationSettings
.AppendElements(mVariationSettings
);
782 fe
->mLanguageOverride
= mLanguageOverride
;
783 fe
->mFamilyName
= mFamilyName
;
784 fe
->mRangeFlags
= mRangeFlags
;
785 fe
->mAscentOverride
= mAscentOverride
;
786 fe
->mDescentOverride
= mDescentOverride
;
787 fe
->mLineGapOverride
= mLineGapOverride
;
788 fe
->mSizeAdjust
= mSizeAdjust
;
789 StoreUserFontData(fe
, aSrcIndex
, fontSet
->GetPrivateBrowsing(),
790 originalFullName
, &metadata
, metaOrigLen
, compression
);
792 ("userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
793 "(%p) gen: %8.8x compress: %d%%\n",
794 fontSet
.get(), aSrcIndex
,
795 mSrcList
[aSrcIndex
].mURI
->GetSpecOrDefault().get(), mFamilyName
.get(),
796 this, uint32_t(fontSet
->mGeneration
), fontCompressionRatio
));
797 mPlatformFontEntry
= fe
;
798 SetLoadState(STATUS_LOADED
);
799 gfxUserFontSet::UserFontCache::CacheFont(fe
);
802 "userfonts (%p) [src %d] failed uri: (%s) for (%s)"
803 " error making platform font\n",
804 fontSet
.get(), aSrcIndex
,
805 mSrcList
[aSrcIndex
].mURI
->GetSpecOrDefault().get(), mFamilyName
.get()));
808 // The downloaded data can now be discarded; the font entry is using the
810 free((void*)aOriginalFontData
);
812 return fe
!= nullptr;
815 void gfxUserFontEntry::Load() {
816 if (mUserFontLoadState
== STATUS_NOT_LOADED
) {
821 void gfxUserFontEntry::IncrementGeneration() {
822 nsTArray
<RefPtr
<gfxUserFontSet
>> fontSets
;
823 GetUserFontSets(fontSets
);
824 for (gfxUserFontSet
* fontSet
: fontSets
) {
825 fontSet
->IncrementGeneration();
829 // This is called when a font download finishes.
830 // Ownership of aFontData passes in here, and the font set must
831 // ensure that it is eventually deleted via free().
832 void gfxUserFontEntry::FontDataDownloadComplete(
833 uint32_t aSrcIndex
, const uint8_t* aFontData
, uint32_t aLength
,
834 nsresult aDownloadStatus
, nsIFontLoadCompleteCallback
* aCallback
) {
835 MOZ_ASSERT(NS_IsMainThread());
837 // forget about the loader, as we no longer potentially need to cancel it
838 // if the entry is obsoleted
841 // download successful, make platform font using font data
842 if (NS_SUCCEEDED(aDownloadStatus
) &&
843 mFontDataLoadingState
!= LOADING_TIMED_OUT
) {
844 LoadPlatformFontAsync(aSrcIndex
, aFontData
, aLength
, aCallback
);
848 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
850 // download failed or font-display timeout passed
851 if (mFontDataLoadingState
== LOADING_TIMED_OUT
) {
852 fontSet
->LogMessage(this, aSrcIndex
,
853 "font-display timeout, webfont not used",
854 nsIScriptError::infoFlag
, aDownloadStatus
);
856 fontSet
->LogMessage(this, aSrcIndex
, "download failed",
857 nsIScriptError::errorFlag
, aDownloadStatus
);
862 free((void*)aFontData
);
865 FontLoadFailed(aCallback
);
868 void gfxUserFontEntry::LoadPlatformFontAsync(
869 uint32_t aSrcIndex
, const uint8_t* aFontData
, uint32_t aLength
,
870 nsIFontLoadCompleteCallback
* aCallback
) {
871 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
> cb(
872 new nsMainThreadPtrHolder
<nsIFontLoadCompleteCallback
>("FontLoader",
875 // Do the OpenType sanitization over on the font loading thread. Once that is
876 // complete, we'll continue in ContinuePlatformFontLoadOnMainThread.
878 // We hold a strong reference to the gfxUserFontSet during this work, since
879 // the document might be closed while we are OMT, and release it at the end
880 // of ContinuePlatformFontLoadOnMainThread.
882 // If the set has already been freed, then the loading will fail when we
883 // resume on the main thread.
885 MOZ_ASSERT(!mLoadingFontSet
);
886 mLoadingFontSet
= GetUserFontSet();
888 nsCOMPtr
<nsIRunnable
> event
=
889 NewRunnableMethod
<uint32_t, const uint8_t*, uint32_t,
890 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
>>(
891 "gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread", this,
892 &gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread
, aSrcIndex
,
893 aFontData
, aLength
, cb
);
894 MOZ_ALWAYS_SUCCEEDS(NS_DispatchBackgroundTask(event
.forget()));
897 void gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread(
898 uint32_t aSrcIndex
, const uint8_t* aOriginalFontData
,
899 uint32_t aOriginalLength
, gfxUserFontType aFontType
,
900 const uint8_t* aSanitizedFontData
, uint32_t aSanitizedLength
,
901 nsTArray
<OTSMessage
>&& aMessages
,
902 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
> aCallback
) {
903 MOZ_ASSERT(NS_IsMainThread());
905 bool loaded
= LoadPlatformFont(aSrcIndex
, aOriginalFontData
, aOriginalLength
,
906 aFontType
, aSanitizedFontData
,
907 aSanitizedLength
, std::move(aMessages
));
908 aOriginalFontData
= nullptr;
909 aSanitizedFontData
= nullptr;
912 IncrementGeneration();
913 aCallback
->FontLoadComplete();
915 FontLoadFailed(aCallback
);
918 // Set in LoadPlatformFontAsync. If it is null, then the font set should have
919 // already been freed and we would not succeed in loading the font.
920 MOZ_ASSERT_IF(loaded
, mLoadingFontSet
);
921 mLoadingFontSet
= nullptr;
924 void gfxUserFontEntry::FontLoadFailed(nsIFontLoadCompleteCallback
* aCallback
) {
925 MOZ_ASSERT(NS_IsMainThread());
927 // Error occurred. Make sure the FontFace's promise is rejected if the
928 // load timed out, or else load the next src.
929 if (mFontDataLoadingState
== LOADING_TIMED_OUT
) {
930 mFontDataLoadingState
= LOADING_FAILED
;
931 SetLoadState(STATUS_FAILED
);
936 // We ignore the status returned by LoadNext();
937 // even if loading failed, we need to bump the font-set generation
938 // and return true in order to trigger reflow, so that fallback
939 // will be used where the text was "masked" by the pending download
940 IncrementGeneration();
941 aCallback
->FontLoadComplete();
944 void gfxUserFontEntry::GetUserFontSets(
945 nsTArray
<RefPtr
<gfxUserFontSet
>>& aResult
) {
947 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
949 aResult
.AppendElement(std::move(fontSet
));
953 gfxUserFontSet::gfxUserFontSet()
955 mRebuildGeneration(0),
956 mLocalRulesUsed(false),
957 mRebuildLocalRules(false),
960 IncrementGeneration(true);
963 gfxUserFontSet::~gfxUserFontSet() { Destroy(); }
965 void gfxUserFontSet::Destroy() {
966 if (auto* pfl
= gfxPlatformFontList::PlatformFontList(false)) {
967 pfl
->RemoveUserFontSet(this);
970 mFontFamilies
.Clear();
973 already_AddRefed
<gfxUserFontEntry
> gfxUserFontSet::FindOrCreateUserFontEntry(
974 nsTArray
<gfxFontFaceSrc
>&& aFontFaceSrcList
,
975 gfxUserFontAttributes
&& aAttr
) {
976 RefPtr
<gfxUserFontEntry
> entry
;
978 // If there's already a userfont entry in the family whose descriptors all
979 // match, we can just move it to the end of the list instead of adding a new
980 // face that will always "shadow" the old one.
981 // Note that we can't do this for platform font entries, even if the
982 // style descriptors match, as they might have had a different source list,
983 // but we no longer have the old source list available to check.
984 RefPtr
<gfxUserFontFamily
> family
= LookupFamily(aAttr
.mFamilyName
);
986 entry
= FindExistingUserFontEntry(family
, aFontFaceSrcList
, aAttr
);
990 entry
= CreateUserFontEntry(std::move(aFontFaceSrcList
), std::move(aAttr
));
993 return entry
.forget();
996 gfxUserFontEntry
* gfxUserFontSet::FindExistingUserFontEntry(
997 gfxUserFontFamily
* aFamily
,
998 const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
999 const gfxUserFontAttributes
& aAttr
) {
1000 aFamily
->ReadLock();
1001 const auto& fontList
= aFamily
->GetFontList();
1002 gfxUserFontEntry
* result
= nullptr;
1004 for (const auto& font
: fontList
) {
1005 if (!font
->mIsUserFontContainer
) {
1009 gfxUserFontEntry
* ufe
= static_cast<gfxUserFontEntry
*>(font
.get());
1010 if (ufe
->Matches(aFontFaceSrcList
, aAttr
)) {
1015 aFamily
->ReadUnlock();
1020 void gfxUserFontSet::AddUserFontEntry(const nsCString
& aFamilyName
,
1021 gfxUserFontEntry
* aUserFontEntry
) {
1022 RefPtr
<gfxUserFontFamily
> family
= GetFamily(aFamilyName
);
1023 family
->AddFontEntry(aUserFontEntry
);
1025 if (LOG_ENABLED()) {
1026 nsAutoCString weightString
;
1027 aUserFontEntry
->Weight().ToString(weightString
);
1028 nsAutoCString stretchString
;
1029 aUserFontEntry
->Stretch().ToString(stretchString
);
1031 ("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
1032 "stretch: %s display: %d",
1033 this, aFamilyName
.get(), aUserFontEntry
,
1034 (aUserFontEntry
->IsItalic()
1036 : (aUserFontEntry
->IsOblique() ? "oblique" : "normal")),
1037 weightString
.get(), stretchString
.get(),
1038 static_cast<int>(aUserFontEntry
->GetFontDisplay())));
1042 void gfxUserFontSet::IncrementGeneration(bool aIsRebuild
) {
1043 // add one, increment again if zero
1045 mGeneration
= ++sFontSetGeneration
;
1046 } while (mGeneration
== 0);
1048 mRebuildGeneration
= mGeneration
;
1052 void gfxUserFontSet::RebuildLocalRules() {
1053 if (mLocalRulesUsed
) {
1054 mRebuildLocalRules
= true;
1055 DoRebuildUserFontSet();
1059 already_AddRefed
<gfxUserFontFamily
> gfxUserFontSet::LookupFamily(
1060 const nsACString
& aFamilyName
) const {
1061 nsAutoCString
key(aFamilyName
);
1064 return mFontFamilies
.Get(key
);
1067 already_AddRefed
<gfxUserFontFamily
> gfxUserFontSet::GetFamily(
1068 const nsACString
& aFamilyName
) {
1069 nsAutoCString
key(aFamilyName
);
1072 return do_AddRef(mFontFamilies
.GetOrInsertNew(key
, aFamilyName
));
1075 void gfxUserFontSet::ForgetLocalFaces() {
1076 for (const auto& fam
: mFontFamilies
.Values()) {
1077 ForgetLocalFace(fam
);
1081 void gfxUserFontSet::ForgetLocalFace(gfxUserFontFamily
* aFontFamily
) {
1082 aFontFamily
->ReadLock();
1083 const auto& fonts
= aFontFamily
->GetFontList();
1084 for (const auto& f
: fonts
) {
1085 auto ufe
= static_cast<gfxUserFontEntry
*>(f
.get());
1086 // If the user font entry has loaded an entry using src:local(),
1087 // discard it as no longer valid.
1088 if (ufe
->GetPlatformFontEntry() &&
1089 ufe
->GetPlatformFontEntry()->IsLocalUserFont()) {
1090 ufe
->mPlatformFontEntry
= nullptr;
1092 // We need to re-evaluate the source list in the context of the new
1093 // platform fontlist, whether or not the entry actually used a local()
1094 // source last time, as one might be newly available.
1095 if (ufe
->mSeenLocalSource
) {
1096 ufe
->LoadCanceled();
1099 aFontFamily
->ReadUnlock();
1102 ///////////////////////////////////////////////////////////////////////////////
1103 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
1104 // across pages/fontsets rather than instantiating new platform fonts.
1106 // Entries are added to this cache when a platform font is instantiated from
1107 // downloaded data, and removed when the platform font entry is destroyed.
1108 // We don't need to use a timed expiration scheme here because the gfxFontEntry
1109 // for a downloaded font will be kept alive by its corresponding gfxFont
1110 // instance(s) until they are deleted, and *that* happens using an expiration
1111 // tracker (gfxFontCache). The result is that the downloaded font instances
1112 // recorded here will persist between pages and can get reused (provided the
1113 // source URI and principal match, of course).
1114 ///////////////////////////////////////////////////////////////////////////////
1116 nsTHashtable
<gfxUserFontSet::UserFontCache::Entry
>*
1117 gfxUserFontSet::UserFontCache::sUserFonts
= nullptr;
1119 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher
, nsIObserver
)
1122 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports
* aSubject
,
1124 const char16_t
* aData
) {
1129 if (!strcmp(aTopic
, "cacheservice:empty-cache")) {
1130 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1133 } else if (!strcmp(aTopic
, "last-pb-context-exited")) {
1134 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1135 if (i
.Get()->IsPrivate()) {
1139 } else if (!strcmp(aTopic
, "xpcom-shutdown")) {
1140 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1141 i
.Get()->GetFontEntry()->DisconnectSVG();
1144 MOZ_ASSERT_UNREACHABLE("unexpected topic");
1150 bool gfxUserFontSet::UserFontCache::Entry::KeyEquals(
1151 const KeyTypePointer aKey
) const {
1152 const gfxFontEntry
* fe
= aKey
->mFontEntry
;
1154 if (!mURI
->Equals(aKey
->mURI
)) {
1158 // For data: URIs, we don't care about the principal; otherwise, check it.
1159 if (!IgnorePrincipal(mURI
)) {
1160 NS_ASSERTION(mPrincipal
&& aKey
->mPrincipal
,
1161 "only data: URIs are allowed to omit the principal");
1162 if (!mPrincipal
->Equals(aKey
->mPrincipal
)) {
1167 if (mPrivate
!= aKey
->mPrivate
) {
1171 if (mFontEntry
->SlantStyle() != fe
->SlantStyle() ||
1172 mFontEntry
->Weight() != fe
->Weight() ||
1173 mFontEntry
->Stretch() != fe
->Stretch() ||
1174 mFontEntry
->mRangeFlags
!= fe
->mRangeFlags
||
1175 mFontEntry
->mFeatureSettings
!= fe
->mFeatureSettings
||
1176 mFontEntry
->mVariationSettings
!= fe
->mVariationSettings
||
1177 mFontEntry
->mLanguageOverride
!= fe
->mLanguageOverride
||
1178 mFontEntry
->mAscentOverride
!= fe
->mAscentOverride
||
1179 mFontEntry
->mDescentOverride
!= fe
->mDescentOverride
||
1180 mFontEntry
->mLineGapOverride
!= fe
->mLineGapOverride
||
1181 mFontEntry
->mSizeAdjust
!= fe
->mSizeAdjust
||
1182 mFontEntry
->mFamilyName
!= fe
->mFamilyName
) {
1189 void gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry
* aFontEntry
) {
1190 NS_ASSERTION(aFontEntry
->mFamilyName
.Length() != 0,
1191 "caching a font associated with no family yet");
1193 // if caching is disabled, simply return
1194 if (Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1198 gfxUserFontData
* data
= aFontEntry
->mUserFontData
.get();
1199 if (data
->mIsBuffer
) {
1200 #ifdef DEBUG_USERFONT_CACHE
1201 printf("userfontcache skipped fontentry with buffer source: %p\n",
1208 sUserFonts
= new nsTHashtable
<Entry
>;
1210 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1212 Flusher
* flusher
= new Flusher
;
1213 obs
->AddObserver(flusher
, "cacheservice:empty-cache", false);
1214 obs
->AddObserver(flusher
, "last-pb-context-exited", false);
1215 obs
->AddObserver(flusher
, "xpcom-shutdown", false);
1218 // Create and register a memory reporter for sUserFonts.
1219 // This reporter is never unregistered, but that's OK because
1220 // the reporter checks whether sUserFonts is null, so it would
1221 // be safe to call even after UserFontCache::Shutdown has deleted
1223 RegisterStrongMemoryReporter(new MemoryReporter());
1226 // For data: URIs, the principal is ignored; anyone who has the same
1227 // data: URI is able to load it and get an equivalent font.
1228 // Otherwise, the principal is used as part of the cache key.
1229 gfxFontSrcPrincipal
* principal
;
1230 if (IgnorePrincipal(data
->mURI
)) {
1231 principal
= nullptr;
1233 principal
= data
->mPrincipal
;
1235 sUserFonts
->PutEntry(Key(data
->mURI
, principal
, aFontEntry
, data
->mPrivate
));
1237 #ifdef DEBUG_USERFONT_CACHE
1238 printf("userfontcache added fontentry: %p\n", aFontEntry
);
1243 void gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry
* aFontEntry
) {
1245 // if we've already deleted the cache (i.e. during shutdown),
1250 // We can't simply use RemoveEntry here because it's possible the principal
1251 // may have changed since the font was cached, in which case the lookup
1252 // would no longer find the entry (bug 838105).
1253 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1254 if (i
.Get()->GetFontEntry() == aFontEntry
) {
1259 #ifdef DEBUG_USERFONT_CACHE
1260 printf("userfontcache removed fontentry: %p\n", aFontEntry
);
1265 gfxFontEntry
* gfxUserFontSet::UserFontCache::GetFont(
1266 const gfxFontFaceSrc
& aSrc
, const gfxUserFontEntry
& aUserFontEntry
) {
1268 Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1272 RefPtr
<gfxUserFontSet
> srcFontSet
= aUserFontEntry
.GetUserFontSet();
1273 if (NS_WARN_IF(!srcFontSet
) || srcFontSet
->BypassCache()) {
1277 // Ignore principal when looking up a data: URI.
1278 RefPtr
<gfxFontSrcPrincipal
> principal
=
1279 IgnorePrincipal(aSrc
.mURI
) ? nullptr : aSrc
.LoadPrincipal(*srcFontSet
);
1281 Entry
* entry
= sUserFonts
->GetEntry(
1282 Key(aSrc
.mURI
, principal
, const_cast<gfxUserFontEntry
*>(&aUserFontEntry
),
1283 srcFontSet
->GetPrivateBrowsing()));
1288 // We have to perform another content policy check here to prevent
1289 // cache poisoning. E.g. a.com loads a font into the cache but
1290 // b.com has a CSP not allowing any fonts to be loaded.
1291 if (!srcFontSet
->IsFontLoadAllowed(aSrc
)) {
1295 return entry
->GetFontEntry();
1298 void gfxUserFontSet::UserFontCache::Shutdown() {
1301 sUserFonts
= nullptr;
1305 MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf
)
1307 void gfxUserFontSet::UserFontCache::Entry::ReportMemory(
1308 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
1310 MOZ_ASSERT(mFontEntry
);
1311 nsAutoCString
path("explicit/gfx/user-fonts/font(");
1314 path
.AppendPrintf("<anonymized-%p>", this);
1316 path
.AppendPrintf("family=%s", mFontEntry
->mFamilyName
.get());
1318 nsCString spec
= mURI
->GetSpecOrDefault();
1319 spec
.ReplaceChar('/', '\\');
1320 // Some fonts are loaded using horrendously-long data: URIs;
1321 // truncate those before reporting them.
1322 if (mURI
->get()->SchemeIs("data") && spec
.Length() > 255) {
1324 spec
.AppendLiteral("...");
1326 path
.AppendPrintf(", url=%s", spec
.get());
1330 mPrincipal
->NodePrincipal()->GetAsciiSpec(spec
);
1331 if (!spec
.IsEmpty()) {
1332 // Include a clue as to who loaded this resource. (Note
1333 // that because of font entry sharing, other pages may now
1334 // be using this resource, and the original page may not
1335 // even be loaded any longer.)
1336 spec
.ReplaceChar('/', '\\');
1337 path
.AppendPrintf(", principal=%s", spec
.get());
1343 aHandleReport
->Callback(
1344 ""_ns
, path
, nsIMemoryReporter::KIND_HEAP
, nsIMemoryReporter::UNITS_BYTES
,
1345 mFontEntry
->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf
),
1346 "Memory used by @font-face resource."_ns
, aData
);
1349 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter
,
1353 gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
1354 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
1360 for (auto it
= sUserFonts
->Iter(); !it
.Done(); it
.Next()) {
1361 it
.Get()->ReportMemory(aHandleReport
, aData
, aAnonymize
);
1365 "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP
, UNITS_BYTES
,
1366 sUserFonts
->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf
),
1367 "Memory used by the @font-face cache, not counting the actual font "
1373 #ifdef DEBUG_USERFONT_CACHE
1375 void gfxUserFontSet::UserFontCache::Entry::Dump() {
1378 nsAutoCString
principalURISpec("(null)");
1379 bool setDomain
= false;
1382 nsCOMPtr
<nsIURI
> principalURI
;
1383 rv
= mPrincipal
->NodePrincipal()->GetURI(getter_AddRefs(principalURI
));
1384 if (NS_SUCCEEDED(rv
)) {
1385 principalURI
->GetSpec(principalURISpec
);
1388 nsCOMPtr
<nsIURI
> domainURI
;
1389 mPrincipal
->NodePrincipal()->GetDomain(getter_AddRefs(domainURI
));
1395 NS_ASSERTION(mURI
, "null URI in userfont cache entry");
1398 "userfontcache fontEntry: %p fonturihash: %8.8x "
1399 "family: %s domainset: %s principal: [%s]\n",
1400 mFontEntry
, mURI
->Hash(), mFontEntry
->FamilyName().get(),
1401 setDomain
? "true" : "false", principalURISpec
.get());
1404 void gfxUserFontSet::UserFontCache::Dump() {
1409 printf("userfontcache dump count: %d ========\n", sUserFonts
->Count());
1410 for (auto it
= sUserFonts
->Iter(); !it
.Done(); it
.Next()) {
1413 printf("userfontcache dump ==================\n");