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 "nsTHashSet.h"
26 using namespace mozilla
;
28 mozilla::LogModule
* gfxUserFontSet::GetUserFontsLog() {
29 static LazyLogModule
sLog("userfonts");
34 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
35 #define LOG_ENABLED() \
36 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug)
38 static Atomic
<uint64_t> sFontSetGeneration(0);
40 gfxUserFontEntry::gfxUserFontEntry(nsTArray
<gfxFontFaceSrc
>&& aFontFaceSrcList
,
41 gfxUserFontAttributes
&& aAttr
)
42 : gfxFontEntry("userfont"_ns
),
43 mUserFontLoadState(STATUS_NOT_LOADED
),
44 mFontDataLoadingState(NOT_LOADING
),
45 mSeenLocalSource(false),
46 mUnsupportedFormat(false),
47 mFontDisplay(aAttr
.mFontDisplay
),
49 mIsUserFontContainer
= true;
50 mSrcList
= std::move(aFontFaceSrcList
);
52 mWeightRange
= aAttr
.mWeight
;
53 mStretchRange
= aAttr
.mStretch
;
54 mStyleRange
= aAttr
.mStyle
;
55 mFeatureSettings
= std::move(aAttr
.mFeatureSettings
);
56 mVariationSettings
= std::move(aAttr
.mVariationSettings
);
57 mLanguageOverride
= aAttr
.mLanguageOverride
;
58 SetUnicodeRangeMap(std::move(aAttr
.mUnicodeRanges
));
59 mRangeFlags
= aAttr
.mRangeFlags
;
60 mAscentOverride
= aAttr
.mAscentOverride
;
61 mDescentOverride
= aAttr
.mDescentOverride
;
62 mLineGapOverride
= aAttr
.mLineGapOverride
;
63 mSizeAdjust
= aAttr
.mSizeAdjust
;
64 mFamilyName
= aAttr
.mFamilyName
;
67 void gfxUserFontEntry::UpdateAttributes(gfxUserFontAttributes
&& aAttr
) {
68 MOZ_ASSERT(NS_IsMainThread());
70 // Remove the entry from the user font cache, if present there, as the cache
71 // key may no longer be correct with the new attributes.
72 gfxUserFontSet::UserFontCache::ForgetFont(this);
74 mFontDisplay
= aAttr
.mFontDisplay
;
75 mWeightRange
= aAttr
.mWeight
;
76 mStretchRange
= aAttr
.mStretch
;
77 mStyleRange
= aAttr
.mStyle
;
78 mFeatureSettings
= std::move(aAttr
.mFeatureSettings
);
79 mVariationSettings
= std::move(aAttr
.mVariationSettings
);
80 mLanguageOverride
= aAttr
.mLanguageOverride
;
81 SetUnicodeRangeMap(std::move(aAttr
.mUnicodeRanges
));
82 mRangeFlags
= aAttr
.mRangeFlags
;
83 mAscentOverride
= aAttr
.mAscentOverride
;
84 mDescentOverride
= aAttr
.mDescentOverride
;
85 mLineGapOverride
= aAttr
.mLineGapOverride
;
86 mSizeAdjust
= aAttr
.mSizeAdjust
;
89 gfxUserFontEntry::~gfxUserFontEntry() {
90 // Assert that we don't drop any gfxUserFontEntry objects during a Servo
91 // traversal, since PostTraversalTask objects can hold raw pointers to
92 // gfxUserFontEntry objects.
93 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
96 bool gfxUserFontEntry::Matches(const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
97 const gfxUserFontAttributes
& aAttr
) {
98 return mWeightRange
== aAttr
.mWeight
&& mStretchRange
== aAttr
.mStretch
&&
99 mStyleRange
== aAttr
.mStyle
&&
100 mFeatureSettings
== aAttr
.mFeatureSettings
&&
101 mVariationSettings
== aAttr
.mVariationSettings
&&
102 mLanguageOverride
== aAttr
.mLanguageOverride
&&
103 mSrcList
== aFontFaceSrcList
&& mFontDisplay
== aAttr
.mFontDisplay
&&
104 mRangeFlags
== aAttr
.mRangeFlags
&&
105 mAscentOverride
== aAttr
.mAscentOverride
&&
106 mDescentOverride
== aAttr
.mDescentOverride
&&
107 mLineGapOverride
== aAttr
.mLineGapOverride
&&
108 mSizeAdjust
== aAttr
.mSizeAdjust
&&
109 ((!aAttr
.mUnicodeRanges
&& !mCharacterMap
) ||
110 (aAttr
.mUnicodeRanges
&& mCharacterMap
&&
111 GetCharacterMap()->Equals(aAttr
.mUnicodeRanges
)));
114 gfxFont
* gfxUserFontEntry::CreateFontInstance(const gfxFontStyle
* aFontStyle
) {
115 MOZ_ASSERT_UNREACHABLE(
116 "should only be creating a gfxFont"
117 " with an actual platform font entry");
119 // userfont entry is a container, can't create font from the container
123 class MOZ_STACK_CLASS gfxOTSMessageContext
: public gfxOTSContext
{
125 virtual ~gfxOTSMessageContext() {
126 MOZ_ASSERT(mMessages
.IsEmpty(), "should have called TakeMessages");
129 virtual void Message(int level
, const char* format
,
130 ...) MSGFUNC_FMT_ATTR override
{
132 va_start(va
, format
);
135 msg
.AppendVprintf(format
, va
);
140 // For warnings (rather than errors that cause the font to fail),
141 // we only report the first instance of any given message.
142 if (!mWarningsIssued
.EnsureInserted(msg
)) {
147 mMessages
.AppendElement(gfxUserFontEntry::OTSMessage
{msg
, level
});
150 bool Process(ots::OTSStream
* aOutput
, const uint8_t* aInput
, size_t aLength
,
151 nsTArray
<gfxUserFontEntry::OTSMessage
>& aMessages
) {
152 bool ok
= ots::OTSContext::Process(aOutput
, aInput
, aLength
);
153 aMessages
= TakeMessages();
157 nsTArray
<gfxUserFontEntry::OTSMessage
>&& TakeMessages() {
158 return std::move(mMessages
);
162 nsTHashSet
<nsCString
> mWarningsIssued
;
163 nsTArray
<gfxUserFontEntry::OTSMessage
> mMessages
;
166 // Call the OTS library to sanitize an sfnt before attempting to use it.
167 // Returns a newly-allocated block, or nullptr in case of fatal errors.
168 const uint8_t* gfxUserFontEntry::SanitizeOpenTypeData(
169 const uint8_t* aData
, uint32_t aLength
, uint32_t& aSaneLength
,
170 gfxUserFontType
& aFontType
, nsTArray
<OTSMessage
>& aMessages
) {
171 aFontType
= gfxFontUtils::DetermineFontDataType(aData
, aLength
);
172 Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE
, uint32_t(aFontType
));
174 size_t lengthHint
= gfxOTSContext::GuessSanitizedFontSize(aLength
, aFontType
);
180 gfxOTSExpandingMemoryStream
<gfxOTSMozAlloc
> output(lengthHint
);
182 gfxOTSMessageContext otsContext
;
183 if (!otsContext
.Process(&output
, aData
, aLength
, aMessages
)) {
184 // Failed to decode/sanitize the font, so discard it.
189 aSaneLength
= output
.Tell();
190 return static_cast<const uint8_t*>(output
.forget());
193 void gfxUserFontEntry::StoreUserFontData(gfxFontEntry
* aFontEntry
,
194 uint32_t aSrcIndex
, bool aPrivate
,
195 const nsACString
& aOriginalName
,
196 FallibleTArray
<uint8_t>* aMetadata
,
197 uint32_t aMetaOrigLen
,
198 uint8_t aCompression
) {
199 if (!aFontEntry
->mUserFontData
) {
200 aFontEntry
->mUserFontData
= MakeUnique
<gfxUserFontData
>();
202 gfxUserFontData
* userFontData
= aFontEntry
->mUserFontData
.get();
203 userFontData
->mSrcIndex
= aSrcIndex
;
204 const gfxFontFaceSrc
& src
= mSrcList
[aSrcIndex
];
205 switch (src
.mSourceType
) {
206 case gfxFontFaceSrc::eSourceType_Local
:
207 userFontData
->mLocalName
= src
.mLocalName
;
209 case gfxFontFaceSrc::eSourceType_URL
:
210 userFontData
->mURI
= src
.mURI
;
211 userFontData
->mPrincipal
= mPrincipal
;
213 case gfxFontFaceSrc::eSourceType_Buffer
:
214 userFontData
->mIsBuffer
= true;
217 userFontData
->mPrivate
= aPrivate
;
218 userFontData
->mTechFlags
= src
.mTechFlags
;
219 userFontData
->mFormatHint
= src
.mFormatHint
;
220 userFontData
->mRealName
= aOriginalName
;
222 userFontData
->mMetadata
= std::move(*aMetadata
);
223 userFontData
->mMetaOrigLen
= aMetaOrigLen
;
224 userFontData
->mCompression
= aCompression
;
228 size_t gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const {
229 return aMallocSizeOf(this) +
230 mMetadata
.ShallowSizeOfExcludingThis(aMallocSizeOf
) +
231 mLocalName
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
) +
232 mRealName
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
233 // Not counting mURI and mPrincipal, as those will be shared.
237 gfxUserFontFamily::~gfxUserFontFamily() {
238 // Should not be dropped by stylo
239 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
242 already_AddRefed
<gfxFontSrcPrincipal
> gfxFontFaceSrc::LoadPrincipal(
243 const gfxUserFontSet
& aFontSet
) const {
244 MOZ_ASSERT(mSourceType
== eSourceType_URL
);
245 if (mUseOriginPrincipal
) {
246 MOZ_ASSERT(mOriginPrincipal
);
247 return RefPtr
{mOriginPrincipal
}.forget();
249 return aFontSet
.GetStandardFontLoadPrincipal();
252 void gfxUserFontEntry::GetFamilyNameAndURIForLogging(uint32_t aSrcIndex
,
253 nsACString
& aFamilyName
,
255 aFamilyName
= mFamilyName
;
258 if (aSrcIndex
>= mSrcList
.Length()) {
259 aURI
.AppendLiteral("(end of source list)");
261 if (mSrcList
[aSrcIndex
].mURI
) {
262 mSrcList
[aSrcIndex
].mURI
->GetSpec(aURI
);
263 // If the source URI was very long, elide the middle of it.
264 // In principle, the byte-oriented chopping here could leave us
265 // with partial UTF-8 characters at the point where we cut it,
266 // but it really doesn't matter as this is just for logging.
267 const uint32_t kMaxURILengthForLogging
= 256;
268 // UTF-8 ellipsis, with spaces to allow additional wrap opportunities
269 // in the resulting log message
270 const char kEllipsis
[] = {' ', '\xE2', '\x80', '\xA6', ' '};
271 if (aURI
.Length() > kMaxURILengthForLogging
) {
272 aURI
.Replace(kMaxURILengthForLogging
/ 2,
273 aURI
.Length() - kMaxURILengthForLogging
, kEllipsis
,
274 ArrayLength(kEllipsis
));
277 aURI
.AppendLiteral("(invalid URI)");
283 AutoSwap_PRUint32 signature
;
284 AutoSwap_PRUint32 flavor
;
285 AutoSwap_PRUint32 length
;
286 AutoSwap_PRUint16 numTables
;
287 AutoSwap_PRUint16 reserved
;
288 AutoSwap_PRUint32 totalSfntSize
;
289 AutoSwap_PRUint16 majorVersion
;
290 AutoSwap_PRUint16 minorVersion
;
291 AutoSwap_PRUint32 metaOffset
;
292 AutoSwap_PRUint32 metaCompLen
;
293 AutoSwap_PRUint32 metaOrigLen
;
294 AutoSwap_PRUint32 privOffset
;
295 AutoSwap_PRUint32 privLen
;
299 AutoSwap_PRUint32 signature
;
300 AutoSwap_PRUint32 flavor
;
301 AutoSwap_PRUint32 length
;
302 AutoSwap_PRUint16 numTables
;
303 AutoSwap_PRUint16 reserved
;
304 AutoSwap_PRUint32 totalSfntSize
;
305 AutoSwap_PRUint32 totalCompressedSize
;
306 AutoSwap_PRUint16 majorVersion
;
307 AutoSwap_PRUint16 minorVersion
;
308 AutoSwap_PRUint32 metaOffset
;
309 AutoSwap_PRUint32 metaCompLen
;
310 AutoSwap_PRUint32 metaOrigLen
;
311 AutoSwap_PRUint32 privOffset
;
312 AutoSwap_PRUint32 privLen
;
315 template <typename HeaderT
>
316 void CopyWOFFMetadata(const uint8_t* aFontData
, uint32_t aLength
,
317 FallibleTArray
<uint8_t>* aMetadata
,
318 uint32_t* aMetaOrigLen
) {
319 // This function may be called with arbitrary, unvalidated "font" data
320 // from @font-face, so it needs to be careful to bounds-check, etc.,
321 // before trying to read anything.
322 // This just saves a copy of the compressed data block; it does NOT check
323 // that the block can be successfully decompressed, or that it contains
324 // well-formed/valid XML metadata.
325 if (aLength
< sizeof(HeaderT
)) {
328 const HeaderT
* woff
= reinterpret_cast<const HeaderT
*>(aFontData
);
329 uint32_t metaOffset
= woff
->metaOffset
;
330 uint32_t metaCompLen
= woff
->metaCompLen
;
331 if (!metaOffset
|| !metaCompLen
|| !woff
->metaOrigLen
) {
334 if (metaOffset
>= aLength
|| metaCompLen
> aLength
- metaOffset
) {
337 if (!aMetadata
->SetLength(woff
->metaCompLen
, fallible
)) {
340 memcpy(aMetadata
->Elements(), aFontData
+ metaOffset
, metaCompLen
);
341 *aMetaOrigLen
= woff
->metaOrigLen
;
344 void gfxUserFontEntry::LoadNextSrc() {
345 NS_ASSERTION(mCurrentSrcIndex
< mSrcList
.Length(),
346 "already at the end of the src list for user font");
347 NS_ASSERTION((mUserFontLoadState
== STATUS_NOT_LOADED
||
348 mUserFontLoadState
== STATUS_LOAD_PENDING
||
349 mUserFontLoadState
== STATUS_LOADING
) &&
350 mFontDataLoadingState
< LOADING_FAILED
,
351 "attempting to load a font that has either completed or failed");
353 if (mUserFontLoadState
== STATUS_NOT_LOADED
) {
354 SetLoadState(STATUS_LOADING
);
355 mFontDataLoadingState
= LOADING_STARTED
;
356 mUnsupportedFormat
= false;
358 // we were already loading; move to the next source,
359 // but don't reset state - if we've already timed out,
360 // that counts against the new download
364 DoLoadNextSrc(false);
367 void gfxUserFontEntry::ContinueLoad() {
368 MOZ_ASSERT(mUserFontLoadState
== STATUS_LOAD_PENDING
);
369 MOZ_ASSERT(mSrcList
[mCurrentSrcIndex
].mSourceType
==
370 gfxFontFaceSrc::eSourceType_URL
);
372 SetLoadState(STATUS_LOADING
);
374 if (LoadState() != STATUS_LOADING
) {
375 MOZ_ASSERT(mUserFontLoadState
!= STATUS_LOAD_PENDING
,
376 "Not in parallel traversal, shouldn't get LOAD_PENDING again");
377 // Loading is synchronously finished (loaded from cache or failed). We
378 // need to increment the generation so that we flush the style data to
379 // use the new loaded font face.
380 // Without parallel traversal, we would simply get the right font data
381 // after the first call to DoLoadNextSrc() in this case, so we don't need
382 // to touch the generation to trigger another restyle.
383 // XXX We may want to return synchronously in parallel traversal in those
384 // cases as well if possible, so that we don't have an additional restyle.
385 // That doesn't work currently because Document::GetDocShell (called from
386 // FontFaceSet::CheckFontLoad) dereferences a weak pointer, which is not
387 // allowed in parallel traversal.
388 IncrementGeneration();
392 static bool IgnorePrincipal(gfxFontSrcURI
* aURI
) {
393 return aURI
->InheritsSecurityContext();
396 void gfxUserFontEntry::DoLoadNextSrc(bool aForceAsync
) {
397 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
398 if (NS_WARN_IF(!fontSet
)) {
399 LOG(("userfonts (%p) failed expired font set for (%s)\n", fontSet
.get(),
401 mFontDataLoadingState
= LOADING_FAILED
;
402 SetLoadState(STATUS_FAILED
);
406 uint32_t numSrc
= mSrcList
.Length();
408 // load each src entry in turn, until a local face is found
409 // or a download begins successfully
410 while (mCurrentSrcIndex
< numSrc
) {
411 gfxFontFaceSrc
& currSrc
= mSrcList
[mCurrentSrcIndex
];
413 // src local ==> lookup and load immediately
415 if (currSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_Local
) {
416 // Don't look up local fonts if the font whitelist is being used.
417 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
418 gfxFontEntry
* fe
= nullptr;
419 if (!pfl
->IsFontFamilyWhitelistActive()) {
420 fe
= gfxPlatform::GetPlatform()->LookupLocalFont(
421 fontSet
->GetPresContext(), currSrc
.mLocalName
, Weight(), Stretch(),
423 // Note that we've attempted a local lookup, even if it failed,
424 // as this means we are dependent on any updates to the font list.
425 mSeenLocalSource
= true;
426 nsTArray
<RefPtr
<gfxUserFontSet
>> fontSets
;
427 GetUserFontSets(fontSets
);
428 for (gfxUserFontSet
* fontSet
: fontSets
) {
429 // We need to note on each gfxUserFontSet that contains the user
430 // font entry that we used a local() rule.
431 fontSet
->SetLocalRulesUsed();
435 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
436 fontSet
.get(), mCurrentSrcIndex
, currSrc
.mLocalName
.get(),
437 mFamilyName
.get(), uint32_t(fontSet
->mGeneration
)));
438 fe
->mFeatureSettings
.AppendElements(mFeatureSettings
);
439 fe
->mVariationSettings
.AppendElements(mVariationSettings
);
440 fe
->mLanguageOverride
= mLanguageOverride
;
441 fe
->mFamilyName
= mFamilyName
;
442 fe
->mRangeFlags
= mRangeFlags
;
443 fe
->mAscentOverride
= mAscentOverride
;
444 fe
->mDescentOverride
= mDescentOverride
;
445 fe
->mLineGapOverride
= mLineGapOverride
;
446 fe
->mSizeAdjust
= mSizeAdjust
;
447 // For src:local(), we don't care whether the request is from
448 // a private window as there's no issue of caching resources;
449 // local fonts are just available all the time.
450 StoreUserFontData(fe
, mCurrentSrcIndex
, false, nsCString(), nullptr, 0,
451 gfxUserFontData::kUnknownCompression
);
452 mPlatformFontEntry
= fe
;
453 SetLoadState(STATUS_LOADED
);
454 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE
,
455 currSrc
.mSourceType
+ 1);
458 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
459 fontSet
.get(), mCurrentSrcIndex
, currSrc
.mLocalName
.get(),
464 // src url ==> start the load process
465 else if (currSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_URL
) {
466 if (gfxPlatform::GetPlatform()->IsFontFormatSupported(
467 currSrc
.mFormatHint
, currSrc
.mTechFlags
)) {
468 if (ServoStyleSet
* set
= gfxFontUtils::CurrentServoStyleSet()) {
469 // Only support style worker threads synchronously getting
470 // entries from the font cache when it's not a data: URI
471 // @font-face that came from UA or user sheets, since we
472 // were not able to call IsFontLoadAllowed ahead of time
473 // for these entries.
474 if (currSrc
.mUseOriginPrincipal
&& IgnorePrincipal(currSrc
.mURI
)) {
475 set
->AppendTask(PostTraversalTask::LoadFontEntry(this));
476 SetLoadState(STATUS_LOAD_PENDING
);
481 // see if we have an existing entry for this source
483 gfxUserFontSet::UserFontCache::GetFont(currSrc
, *this);
485 mPlatformFontEntry
= fe
;
486 SetLoadState(STATUS_LOADED
);
489 ("userfonts (%p) [src %d] "
490 "loaded uri from cache: (%s) for (%s)\n",
491 fontSet
.get(), mCurrentSrcIndex
,
492 currSrc
.mURI
->GetSpecOrDefault().get(), mFamilyName
.get()));
497 if (ServoStyleSet
* set
= gfxFontUtils::CurrentServoStyleSet()) {
498 // If we need to start a font load and we're on a style
499 // worker thread, we have to defer it.
500 set
->AppendTask(PostTraversalTask::LoadFontEntry(this));
501 SetLoadState(STATUS_LOAD_PENDING
);
505 // record the principal we should use for the load for use when
506 // creating a channel and when caching the loaded entry.
507 mPrincipal
= currSrc
.LoadPrincipal(*fontSet
);
509 bool loadDoesntSpin
= !aForceAsync
&& currSrc
.mURI
->SyncLoadIsOK();
511 if (loadDoesntSpin
) {
512 uint8_t* buffer
= nullptr;
513 uint32_t bufferLength
= 0;
515 // sync load font immediately
517 fontSet
->SyncLoadFontData(this, &currSrc
, buffer
, bufferLength
);
519 if (NS_SUCCEEDED(rv
) &&
520 LoadPlatformFontSync(mCurrentSrcIndex
, buffer
, bufferLength
)) {
521 SetLoadState(STATUS_LOADED
);
522 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE
,
523 currSrc
.mSourceType
+ 1);
526 fontSet
->LogMessage(this, mCurrentSrcIndex
, "font load failed",
527 nsIScriptError::errorFlag
, rv
);
531 // otherwise load font async
532 nsresult rv
= fontSet
->StartLoad(this, mCurrentSrcIndex
);
533 bool loadOK
= NS_SUCCEEDED(rv
);
537 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
538 fontSet
.get(), mCurrentSrcIndex
,
539 currSrc
.mURI
->GetSpecOrDefault().get(), mFamilyName
.get()));
543 fontSet
->LogMessage(this, mCurrentSrcIndex
,
544 "failed to start download",
545 nsIScriptError::errorFlag
, rv
);
549 // We don't log a warning to the web console yet,
550 // as another source may load successfully
551 mUnsupportedFormat
= true;
555 // FontFace buffer ==> load immediately
558 MOZ_ASSERT(currSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_Buffer
);
560 uint8_t* buffer
= nullptr;
561 uint32_t bufferLength
= 0;
563 // sync load font immediately
564 currSrc
.mBuffer
->TakeBuffer(buffer
, bufferLength
);
566 LoadPlatformFontSync(mCurrentSrcIndex
, buffer
, bufferLength
)) {
567 // LoadPlatformFontSync takes ownership of the buffer, so no need
569 SetLoadState(STATUS_LOADED
);
570 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE
,
571 currSrc
.mSourceType
+ 1);
574 fontSet
->LogMessage(this, mCurrentSrcIndex
, "font load failed",
575 nsIScriptError::errorFlag
);
582 if (mUnsupportedFormat
) {
583 fontSet
->LogMessage(this, mCurrentSrcIndex
, "no supported format found",
584 nsIScriptError::warningFlag
);
587 // all src's failed; mark this entry as unusable (so fallback will occur)
588 LOG(("userfonts (%p) failed all src for (%s)\n", fontSet
.get(),
590 mFontDataLoadingState
= LOADING_FAILED
;
591 SetLoadState(STATUS_FAILED
);
594 void gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState
) {
595 mUserFontLoadState
= aLoadState
;
598 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc
)
600 bool gfxUserFontEntry::LoadPlatformFontSync(uint32_t aSrcIndex
,
601 const uint8_t* aFontData
,
603 AUTO_PROFILER_LABEL("gfxUserFontEntry::LoadPlatformFontSync", OTHER
);
604 NS_ASSERTION((mUserFontLoadState
== STATUS_NOT_LOADED
||
605 mUserFontLoadState
== STATUS_LOAD_PENDING
||
606 mUserFontLoadState
== STATUS_LOADING
) &&
607 mFontDataLoadingState
< LOADING_FAILED
,
608 "attempting to load a font that has either completed or failed");
610 // Unwrap/decompress/sanitize or otherwise munge the downloaded data
611 // to make a usable sfnt structure.
613 // Call the OTS sanitizer; this will also decode WOFF to sfnt
614 // if necessary. The original data in aFontData is left unchanged.
616 gfxUserFontType fontType
;
617 nsTArray
<OTSMessage
> messages
;
618 const uint8_t* saneData
=
619 SanitizeOpenTypeData(aFontData
, aLength
, saneLen
, fontType
, messages
);
621 return LoadPlatformFont(aSrcIndex
, aFontData
, aLength
, fontType
, saneData
,
622 saneLen
, std::move(messages
));
625 void gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread(
626 uint32_t aSrcIndex
, const uint8_t* aFontData
, uint32_t aLength
,
627 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
> aCallback
) {
628 MOZ_ASSERT(!NS_IsMainThread());
631 gfxUserFontType fontType
;
632 nsTArray
<OTSMessage
> messages
;
633 const uint8_t* saneData
=
634 SanitizeOpenTypeData(aFontData
, aLength
, saneLen
, fontType
, messages
);
636 nsCOMPtr
<nsIRunnable
> event
=
637 NewRunnableMethod
<uint32_t, const uint8_t*, uint32_t, gfxUserFontType
,
638 const uint8_t*, uint32_t, nsTArray
<OTSMessage
>&&,
639 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
>>(
640 "gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread", this,
641 &gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread
, aSrcIndex
,
642 aFontData
, aLength
, fontType
, saneData
, saneLen
, std::move(messages
),
644 NS_DispatchToMainThread(event
.forget());
647 bool gfxUserFontEntry::LoadPlatformFont(uint32_t aSrcIndex
,
648 const uint8_t* aOriginalFontData
,
649 uint32_t aOriginalLength
,
650 gfxUserFontType aFontType
,
651 const uint8_t* aSanitizedFontData
,
652 uint32_t aSanitizedLength
,
653 nsTArray
<OTSMessage
>&& aMessages
) {
654 MOZ_ASSERT(NS_IsMainThread());
655 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
656 if (NS_WARN_IF(!fontSet
)) {
657 free((void*)aOriginalFontData
);
661 for (const auto& msg
: aMessages
) {
662 fontSet
->LogMessage(this, aSrcIndex
, msg
.mMessage
.get(),
663 msg
.mLevel
> 0 ? nsIScriptError::warningFlag
664 : nsIScriptError::errorFlag
);
667 if (!aSanitizedFontData
) {
668 fontSet
->LogMessage(this, aSrcIndex
, "rejected by sanitizer");
670 // Check whether aSanitizedFontData is a known OpenType format; it might be
671 // a TrueType Collection, which OTS would accept but we don't yet
672 // know how to handle. If so, discard.
673 if (gfxFontUtils::DetermineFontDataType(
674 aSanitizedFontData
, aSanitizedLength
) != GFX_USERFONT_OPENTYPE
) {
675 fontSet
->LogMessage(this, aSrcIndex
, "not a supported OpenType format");
676 free((void*)aSanitizedFontData
);
677 aSanitizedFontData
= nullptr;
681 // Because platform font activation code may replace the name table
682 // in the font with a synthetic one, we save the original name so that
683 // it can be reported via the InspectorUtils API.
684 nsAutoCString originalFullName
;
686 gfxFontEntry
* fe
= nullptr;
687 uint32_t fontCompressionRatio
= 0;
688 size_t computedSize
= 0;
690 if (aSanitizedFontData
) {
691 if (aSanitizedLength
) {
692 fontCompressionRatio
=
693 uint32_t(100.0 * aOriginalLength
/ aSanitizedLength
+ 0.5);
694 if (aFontType
== GFX_USERFONT_WOFF
|| aFontType
== GFX_USERFONT_WOFF2
) {
695 Telemetry::Accumulate(aFontType
== GFX_USERFONT_WOFF
696 ? Telemetry::WEBFONT_COMPRESSION_WOFF
697 : Telemetry::WEBFONT_COMPRESSION_WOFF2
,
698 fontCompressionRatio
);
702 // The sanitizer ensures that we have a valid sfnt and a usable
703 // name table, so this should never fail unless we're out of
704 // memory, and GetFullNameFromSFNT is not directly exposed to
705 // arbitrary/malicious data from the web.
706 gfxFontUtils::GetFullNameFromSFNT(aSanitizedFontData
, aSanitizedLength
,
709 // Record size for memory reporting purposes. We measure this now
710 // because by the time we potentially want to collect reports, this
711 // data block may have been handed off to opaque OS font APIs that
712 // don't allow us to retrieve or measure it directly.
713 // The *OnAlloc function will also tell DMD about this block, as the
714 // OS font code may hold on to it for an extended period.
715 computedSize
= UserFontMallocSizeOfOnAlloc(aSanitizedFontData
);
717 // Here ownership of aSanitizedFontData is passed to the platform,
718 // which will delete it when no longer required
719 fe
= gfxPlatform::GetPlatform()->MakePlatformFont(
720 mName
, Weight(), Stretch(), SlantStyle(), aSanitizedFontData
,
723 fontSet
->LogMessage(this, aSrcIndex
, "not usable by platform");
728 fe
->mComputedSizeOfUserFont
= computedSize
;
730 // Save a copy of the metadata block (if present) for InspectorUtils
731 // to use if required. Ownership of the metadata block will be passed
732 // to the gfxUserFontData record below.
733 FallibleTArray
<uint8_t> metadata
;
734 uint32_t metaOrigLen
= 0;
735 uint8_t compression
= gfxUserFontData::kUnknownCompression
;
736 if (aFontType
== GFX_USERFONT_WOFF
) {
737 CopyWOFFMetadata
<WOFFHeader
>(aOriginalFontData
, aOriginalLength
,
738 &metadata
, &metaOrigLen
);
739 compression
= gfxUserFontData::kZlibCompression
;
740 } else if (aFontType
== GFX_USERFONT_WOFF2
) {
741 CopyWOFFMetadata
<WOFF2Header
>(aOriginalFontData
, aOriginalLength
,
742 &metadata
, &metaOrigLen
);
743 compression
= gfxUserFontData::kBrotliCompression
;
746 // copy OpenType feature/language settings from the userfont entry to the
747 // newly-created font entry
748 fe
->mFeatureSettings
.AppendElements(mFeatureSettings
);
749 fe
->mVariationSettings
.AppendElements(mVariationSettings
);
750 fe
->mLanguageOverride
= mLanguageOverride
;
751 fe
->mFamilyName
= mFamilyName
;
752 fe
->mRangeFlags
= mRangeFlags
;
753 fe
->mAscentOverride
= mAscentOverride
;
754 fe
->mDescentOverride
= mDescentOverride
;
755 fe
->mLineGapOverride
= mLineGapOverride
;
756 fe
->mSizeAdjust
= mSizeAdjust
;
757 StoreUserFontData(fe
, aSrcIndex
, fontSet
->GetPrivateBrowsing(),
758 originalFullName
, &metadata
, metaOrigLen
, compression
);
761 "userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
762 "(%p) gen: %8.8x compress: %d%%\n",
763 fontSet
.get(), aSrcIndex
,
764 mSrcList
[aSrcIndex
].mURI
->GetSpecOrDefault().get(), mFamilyName
.get(),
765 this, uint32_t(fontSet
->mGeneration
), fontCompressionRatio
));
767 mPlatformFontEntry
= fe
;
768 SetLoadState(STATUS_LOADED
);
769 gfxUserFontSet::UserFontCache::CacheFont(fe
);
773 ("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
774 " error making platform font\n",
775 fontSet
.get(), aSrcIndex
,
776 mSrcList
[aSrcIndex
].mURI
->GetSpecOrDefault().get(),
781 // The downloaded data can now be discarded; the font entry is using the
783 free((void*)aOriginalFontData
);
785 return fe
!= nullptr;
788 void gfxUserFontEntry::Load() {
789 if (mUserFontLoadState
== STATUS_NOT_LOADED
) {
794 void gfxUserFontEntry::IncrementGeneration() {
795 nsTArray
<RefPtr
<gfxUserFontSet
>> fontSets
;
796 GetUserFontSets(fontSets
);
797 for (gfxUserFontSet
* fontSet
: fontSets
) {
798 fontSet
->IncrementGeneration();
802 // This is called when a font download finishes.
803 // Ownership of aFontData passes in here, and the font set must
804 // ensure that it is eventually deleted via free().
805 void gfxUserFontEntry::FontDataDownloadComplete(
806 uint32_t aSrcIndex
, const uint8_t* aFontData
, uint32_t aLength
,
807 nsresult aDownloadStatus
, nsIFontLoadCompleteCallback
* aCallback
) {
808 MOZ_ASSERT(NS_IsMainThread());
810 // forget about the loader, as we no longer potentially need to cancel it
811 // if the entry is obsoleted
814 // download successful, make platform font using font data
815 if (NS_SUCCEEDED(aDownloadStatus
) &&
816 mFontDataLoadingState
!= LOADING_TIMED_OUT
) {
817 if (StaticPrefs::gfx_downloadable_fonts_sanitize_omt()) {
818 LoadPlatformFontAsync(aSrcIndex
, aFontData
, aLength
, aCallback
);
820 bool loaded
= LoadPlatformFontSync(aSrcIndex
, aFontData
, aLength
);
823 IncrementGeneration();
824 aCallback
->FontLoadComplete();
826 FontLoadFailed(aCallback
);
832 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
834 // download failed or font-display timeout passed
835 if (mFontDataLoadingState
== LOADING_TIMED_OUT
) {
836 fontSet
->LogMessage(this, aSrcIndex
,
837 "font-display timeout, webfont not used",
838 nsIScriptError::infoFlag
, aDownloadStatus
);
840 fontSet
->LogMessage(this, aSrcIndex
, "download failed",
841 nsIScriptError::errorFlag
, aDownloadStatus
);
846 free((void*)aFontData
);
849 FontLoadFailed(aCallback
);
852 void gfxUserFontEntry::LoadPlatformFontAsync(
853 uint32_t aSrcIndex
, const uint8_t* aFontData
, uint32_t aLength
,
854 nsIFontLoadCompleteCallback
* aCallback
) {
855 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
> cb(
856 new nsMainThreadPtrHolder
<nsIFontLoadCompleteCallback
>("FontLoader",
859 // Do the OpenType sanitization over on the font loading thread. Once that is
860 // complete, we'll continue in ContinuePlatformFontLoadOnMainThread.
862 // We hold a strong reference to the gfxUserFontSet during this work, since
863 // the document might be closed while we are OMT, and release it at the end
864 // of ContinuePlatformFontLoadOnMainThread.
866 // If the set has already been freed, then the loading will fail when we
867 // resume on the main thread.
869 MOZ_ASSERT(!mLoadingFontSet
);
870 mLoadingFontSet
= GetUserFontSet();
872 nsCOMPtr
<nsIRunnable
> event
=
873 NewRunnableMethod
<uint32_t, const uint8_t*, uint32_t,
874 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
>>(
875 "gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread", this,
876 &gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread
, aSrcIndex
,
877 aFontData
, aLength
, cb
);
878 MOZ_ALWAYS_SUCCEEDS(NS_DispatchBackgroundTask(event
.forget()));
881 void gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread(
882 uint32_t aSrcIndex
, const uint8_t* aOriginalFontData
,
883 uint32_t aOriginalLength
, gfxUserFontType aFontType
,
884 const uint8_t* aSanitizedFontData
, uint32_t aSanitizedLength
,
885 nsTArray
<OTSMessage
>&& aMessages
,
886 nsMainThreadPtrHandle
<nsIFontLoadCompleteCallback
> aCallback
) {
887 MOZ_ASSERT(NS_IsMainThread());
889 bool loaded
= LoadPlatformFont(aSrcIndex
, aOriginalFontData
, aOriginalLength
,
890 aFontType
, aSanitizedFontData
,
891 aSanitizedLength
, std::move(aMessages
));
892 aOriginalFontData
= nullptr;
893 aSanitizedFontData
= nullptr;
896 IncrementGeneration();
897 aCallback
->FontLoadComplete();
899 FontLoadFailed(aCallback
);
902 // Set in LoadPlatformFontAsync. If it is null, then the font set should have
903 // already been freed and we would not succeed in loading the font.
904 MOZ_ASSERT_IF(loaded
, mLoadingFontSet
);
905 mLoadingFontSet
= nullptr;
908 void gfxUserFontEntry::FontLoadFailed(nsIFontLoadCompleteCallback
* aCallback
) {
909 MOZ_ASSERT(NS_IsMainThread());
911 // Error occurred. Make sure the FontFace's promise is rejected if the
912 // load timed out, or else load the next src.
913 if (mFontDataLoadingState
== LOADING_TIMED_OUT
) {
914 mFontDataLoadingState
= LOADING_FAILED
;
915 SetLoadState(STATUS_FAILED
);
920 // We ignore the status returned by LoadNext();
921 // even if loading failed, we need to bump the font-set generation
922 // and return true in order to trigger reflow, so that fallback
923 // will be used where the text was "masked" by the pending download
924 IncrementGeneration();
925 aCallback
->FontLoadComplete();
928 void gfxUserFontEntry::GetUserFontSets(
929 nsTArray
<RefPtr
<gfxUserFontSet
>>& aResult
) {
931 RefPtr
<gfxUserFontSet
> fontSet
= GetUserFontSet();
933 aResult
.AppendElement(std::move(fontSet
));
937 gfxUserFontSet::gfxUserFontSet()
939 mRebuildGeneration(0),
940 mLocalRulesUsed(false),
941 mRebuildLocalRules(false),
944 IncrementGeneration(true);
945 gfxPlatformFontList
* fp
= gfxPlatformFontList::PlatformFontList();
947 fp
->AddUserFontSet(this);
951 gfxUserFontSet::~gfxUserFontSet() { Destroy(); }
953 void gfxUserFontSet::Destroy() {
954 gfxPlatformFontList
* fp
= gfxPlatformFontList::PlatformFontList();
956 fp
->RemoveUserFontSet(this);
959 mFontFamilies
.Clear();
962 already_AddRefed
<gfxUserFontEntry
> gfxUserFontSet::FindOrCreateUserFontEntry(
963 nsTArray
<gfxFontFaceSrc
>&& aFontFaceSrcList
,
964 gfxUserFontAttributes
&& aAttr
) {
965 RefPtr
<gfxUserFontEntry
> entry
;
967 // If there's already a userfont entry in the family whose descriptors all
968 // match, we can just move it to the end of the list instead of adding a new
969 // face that will always "shadow" the old one.
970 // Note that we can't do this for platform font entries, even if the
971 // style descriptors match, as they might have had a different source list,
972 // but we no longer have the old source list available to check.
973 RefPtr
<gfxUserFontFamily
> family
= LookupFamily(aAttr
.mFamilyName
);
975 entry
= FindExistingUserFontEntry(family
, aFontFaceSrcList
, aAttr
);
979 entry
= CreateUserFontEntry(std::move(aFontFaceSrcList
), std::move(aAttr
));
982 return entry
.forget();
985 gfxUserFontEntry
* gfxUserFontSet::FindExistingUserFontEntry(
986 gfxUserFontFamily
* aFamily
,
987 const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
988 const gfxUserFontAttributes
& aAttr
) {
990 const auto& fontList
= aFamily
->GetFontList();
991 gfxUserFontEntry
* result
= nullptr;
993 for (const auto& font
: fontList
) {
994 if (!font
->mIsUserFontContainer
) {
998 gfxUserFontEntry
* ufe
= static_cast<gfxUserFontEntry
*>(font
.get());
999 if (ufe
->Matches(aFontFaceSrcList
, aAttr
)) {
1004 aFamily
->ReadUnlock();
1009 void gfxUserFontSet::AddUserFontEntry(const nsCString
& aFamilyName
,
1010 gfxUserFontEntry
* aUserFontEntry
) {
1011 RefPtr
<gfxUserFontFamily
> family
= GetFamily(aFamilyName
);
1012 family
->AddFontEntry(aUserFontEntry
);
1014 if (LOG_ENABLED()) {
1015 nsAutoCString weightString
;
1016 aUserFontEntry
->Weight().ToString(weightString
);
1017 nsAutoCString stretchString
;
1018 aUserFontEntry
->Stretch().ToString(stretchString
);
1020 ("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
1021 "stretch: %s display: %d",
1022 this, aFamilyName
.get(), aUserFontEntry
,
1023 (aUserFontEntry
->IsItalic()
1025 : (aUserFontEntry
->IsOblique() ? "oblique" : "normal")),
1026 weightString
.get(), stretchString
.get(),
1027 static_cast<int>(aUserFontEntry
->GetFontDisplay())));
1031 void gfxUserFontSet::IncrementGeneration(bool aIsRebuild
) {
1032 // add one, increment again if zero
1034 mGeneration
= ++sFontSetGeneration
;
1035 } while (mGeneration
== 0);
1037 mRebuildGeneration
= mGeneration
;
1041 void gfxUserFontSet::RebuildLocalRules() {
1042 if (mLocalRulesUsed
) {
1043 mRebuildLocalRules
= true;
1044 DoRebuildUserFontSet();
1048 already_AddRefed
<gfxUserFontFamily
> gfxUserFontSet::LookupFamily(
1049 const nsACString
& aFamilyName
) const {
1050 nsAutoCString
key(aFamilyName
);
1053 return mFontFamilies
.Get(key
);
1056 already_AddRefed
<gfxUserFontFamily
> gfxUserFontSet::GetFamily(
1057 const nsACString
& aFamilyName
) {
1058 nsAutoCString
key(aFamilyName
);
1061 return do_AddRef(mFontFamilies
.GetOrInsertNew(key
, aFamilyName
));
1064 void gfxUserFontSet::ForgetLocalFaces() {
1065 for (const auto& fam
: mFontFamilies
.Values()) {
1066 ForgetLocalFace(fam
);
1070 void gfxUserFontSet::ForgetLocalFace(gfxUserFontFamily
* aFontFamily
) {
1071 aFontFamily
->ReadLock();
1072 const auto& fonts
= aFontFamily
->GetFontList();
1073 for (const auto& f
: fonts
) {
1074 auto ufe
= static_cast<gfxUserFontEntry
*>(f
.get());
1075 // If the user font entry has loaded an entry using src:local(),
1076 // discard it as no longer valid.
1077 if (ufe
->GetPlatformFontEntry() &&
1078 ufe
->GetPlatformFontEntry()->IsLocalUserFont()) {
1079 ufe
->mPlatformFontEntry
= nullptr;
1081 // We need to re-evaluate the source list in the context of the new
1082 // platform fontlist, whether or not the entry actually used a local()
1083 // source last time, as one might be newly available.
1084 if (ufe
->mSeenLocalSource
) {
1085 ufe
->LoadCanceled();
1088 aFontFamily
->ReadUnlock();
1091 ///////////////////////////////////////////////////////////////////////////////
1092 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
1093 // across pages/fontsets rather than instantiating new platform fonts.
1095 // Entries are added to this cache when a platform font is instantiated from
1096 // downloaded data, and removed when the platform font entry is destroyed.
1097 // We don't need to use a timed expiration scheme here because the gfxFontEntry
1098 // for a downloaded font will be kept alive by its corresponding gfxFont
1099 // instance(s) until they are deleted, and *that* happens using an expiration
1100 // tracker (gfxFontCache). The result is that the downloaded font instances
1101 // recorded here will persist between pages and can get reused (provided the
1102 // source URI and principal match, of course).
1103 ///////////////////////////////////////////////////////////////////////////////
1105 nsTHashtable
<gfxUserFontSet::UserFontCache::Entry
>*
1106 gfxUserFontSet::UserFontCache::sUserFonts
= nullptr;
1108 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher
, nsIObserver
)
1111 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports
* aSubject
,
1113 const char16_t
* aData
) {
1118 if (!strcmp(aTopic
, "cacheservice:empty-cache")) {
1119 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1122 } else if (!strcmp(aTopic
, "last-pb-context-exited")) {
1123 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1124 if (i
.Get()->IsPrivate()) {
1128 } else if (!strcmp(aTopic
, "xpcom-shutdown")) {
1129 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1130 i
.Get()->GetFontEntry()->DisconnectSVG();
1133 MOZ_ASSERT_UNREACHABLE("unexpected topic");
1139 bool gfxUserFontSet::UserFontCache::Entry::KeyEquals(
1140 const KeyTypePointer aKey
) const {
1141 const gfxFontEntry
* fe
= aKey
->mFontEntry
;
1143 if (!mURI
->Equals(aKey
->mURI
)) {
1147 // For data: URIs, we don't care about the principal; otherwise, check it.
1148 if (!IgnorePrincipal(mURI
)) {
1149 NS_ASSERTION(mPrincipal
&& aKey
->mPrincipal
,
1150 "only data: URIs are allowed to omit the principal");
1151 if (!mPrincipal
->Equals(aKey
->mPrincipal
)) {
1156 if (mPrivate
!= aKey
->mPrivate
) {
1160 if (mFontEntry
->SlantStyle() != fe
->SlantStyle() ||
1161 mFontEntry
->Weight() != fe
->Weight() ||
1162 mFontEntry
->Stretch() != fe
->Stretch() ||
1163 mFontEntry
->mRangeFlags
!= fe
->mRangeFlags
||
1164 mFontEntry
->mFeatureSettings
!= fe
->mFeatureSettings
||
1165 mFontEntry
->mVariationSettings
!= fe
->mVariationSettings
||
1166 mFontEntry
->mLanguageOverride
!= fe
->mLanguageOverride
||
1167 mFontEntry
->mAscentOverride
!= fe
->mAscentOverride
||
1168 mFontEntry
->mDescentOverride
!= fe
->mDescentOverride
||
1169 mFontEntry
->mLineGapOverride
!= fe
->mLineGapOverride
||
1170 mFontEntry
->mSizeAdjust
!= fe
->mSizeAdjust
||
1171 mFontEntry
->mFamilyName
!= fe
->mFamilyName
) {
1178 void gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry
* aFontEntry
) {
1179 NS_ASSERTION(aFontEntry
->mFamilyName
.Length() != 0,
1180 "caching a font associated with no family yet");
1182 // if caching is disabled, simply return
1183 if (Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1187 gfxUserFontData
* data
= aFontEntry
->mUserFontData
.get();
1188 if (data
->mIsBuffer
) {
1189 #ifdef DEBUG_USERFONT_CACHE
1190 printf("userfontcache skipped fontentry with buffer source: %p\n",
1197 sUserFonts
= new nsTHashtable
<Entry
>;
1199 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
1201 Flusher
* flusher
= new Flusher
;
1202 obs
->AddObserver(flusher
, "cacheservice:empty-cache", false);
1203 obs
->AddObserver(flusher
, "last-pb-context-exited", false);
1204 obs
->AddObserver(flusher
, "xpcom-shutdown", false);
1207 // Create and register a memory reporter for sUserFonts.
1208 // This reporter is never unregistered, but that's OK because
1209 // the reporter checks whether sUserFonts is null, so it would
1210 // be safe to call even after UserFontCache::Shutdown has deleted
1212 RegisterStrongMemoryReporter(new MemoryReporter());
1215 // For data: URIs, the principal is ignored; anyone who has the same
1216 // data: URI is able to load it and get an equivalent font.
1217 // Otherwise, the principal is used as part of the cache key.
1218 gfxFontSrcPrincipal
* principal
;
1219 if (IgnorePrincipal(data
->mURI
)) {
1220 principal
= nullptr;
1222 principal
= data
->mPrincipal
;
1224 sUserFonts
->PutEntry(Key(data
->mURI
, principal
, aFontEntry
, data
->mPrivate
));
1226 #ifdef DEBUG_USERFONT_CACHE
1227 printf("userfontcache added fontentry: %p\n", aFontEntry
);
1232 void gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry
* aFontEntry
) {
1234 // if we've already deleted the cache (i.e. during shutdown),
1239 // We can't simply use RemoveEntry here because it's possible the principal
1240 // may have changed since the font was cached, in which case the lookup
1241 // would no longer find the entry (bug 838105).
1242 for (auto i
= sUserFonts
->Iter(); !i
.Done(); i
.Next()) {
1243 if (i
.Get()->GetFontEntry() == aFontEntry
) {
1248 #ifdef DEBUG_USERFONT_CACHE
1249 printf("userfontcache removed fontentry: %p\n", aFontEntry
);
1254 gfxFontEntry
* gfxUserFontSet::UserFontCache::GetFont(
1255 const gfxFontFaceSrc
& aSrc
, const gfxUserFontEntry
& aUserFontEntry
) {
1257 Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1261 RefPtr
<gfxUserFontSet
> srcFontSet
= aUserFontEntry
.GetUserFontSet();
1262 if (NS_WARN_IF(!srcFontSet
) || srcFontSet
->BypassCache()) {
1266 // Ignore principal when looking up a data: URI.
1267 RefPtr
<gfxFontSrcPrincipal
> principal
=
1268 IgnorePrincipal(aSrc
.mURI
) ? nullptr : aSrc
.LoadPrincipal(*srcFontSet
);
1270 Entry
* entry
= sUserFonts
->GetEntry(
1271 Key(aSrc
.mURI
, principal
, const_cast<gfxUserFontEntry
*>(&aUserFontEntry
),
1272 srcFontSet
->GetPrivateBrowsing()));
1277 // We have to perform another content policy check here to prevent
1278 // cache poisoning. E.g. a.com loads a font into the cache but
1279 // b.com has a CSP not allowing any fonts to be loaded.
1280 if (!srcFontSet
->IsFontLoadAllowed(aSrc
)) {
1284 return entry
->GetFontEntry();
1287 void gfxUserFontSet::UserFontCache::Shutdown() {
1290 sUserFonts
= nullptr;
1294 MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf
)
1296 void gfxUserFontSet::UserFontCache::Entry::ReportMemory(
1297 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
1299 MOZ_ASSERT(mFontEntry
);
1300 nsAutoCString
path("explicit/gfx/user-fonts/font(");
1303 path
.AppendPrintf("<anonymized-%p>", this);
1305 path
.AppendPrintf("family=%s", mFontEntry
->mFamilyName
.get());
1307 nsCString spec
= mURI
->GetSpecOrDefault();
1308 spec
.ReplaceChar('/', '\\');
1309 // Some fonts are loaded using horrendously-long data: URIs;
1310 // truncate those before reporting them.
1311 if (mURI
->get()->SchemeIs("data") && spec
.Length() > 255) {
1313 spec
.AppendLiteral("...");
1315 path
.AppendPrintf(", url=%s", spec
.get());
1319 mPrincipal
->NodePrincipal()->GetAsciiSpec(spec
);
1320 if (!spec
.IsEmpty()) {
1321 // Include a clue as to who loaded this resource. (Note
1322 // that because of font entry sharing, other pages may now
1323 // be using this resource, and the original page may not
1324 // even be loaded any longer.)
1325 spec
.ReplaceChar('/', '\\');
1326 path
.AppendPrintf(", principal=%s", spec
.get());
1332 aHandleReport
->Callback(
1333 ""_ns
, path
, nsIMemoryReporter::KIND_HEAP
, nsIMemoryReporter::UNITS_BYTES
,
1334 mFontEntry
->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf
),
1335 "Memory used by @font-face resource."_ns
, aData
);
1338 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter
,
1342 gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
1343 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
,
1349 for (auto it
= sUserFonts
->Iter(); !it
.Done(); it
.Next()) {
1350 it
.Get()->ReportMemory(aHandleReport
, aData
, aAnonymize
);
1354 "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP
, UNITS_BYTES
,
1355 sUserFonts
->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf
),
1356 "Memory used by the @font-face cache, not counting the actual font "
1362 #ifdef DEBUG_USERFONT_CACHE
1364 void gfxUserFontSet::UserFontCache::Entry::Dump() {
1367 nsAutoCString
principalURISpec("(null)");
1368 bool setDomain
= false;
1371 nsCOMPtr
<nsIURI
> principalURI
;
1372 rv
= mPrincipal
->NodePrincipal()->GetURI(getter_AddRefs(principalURI
));
1373 if (NS_SUCCEEDED(rv
)) {
1374 principalURI
->GetSpec(principalURISpec
);
1377 nsCOMPtr
<nsIURI
> domainURI
;
1378 mPrincipal
->NodePrincipal()->GetDomain(getter_AddRefs(domainURI
));
1384 NS_ASSERTION(mURI
, "null URI in userfont cache entry");
1387 "userfontcache fontEntry: %p fonturihash: %8.8x "
1388 "family: %s domainset: %s principal: [%s]\n",
1389 mFontEntry
, mURI
->Hash(), mFontEntry
->FamilyName().get(),
1390 setDomain
? "true" : "false", principalURISpec
.get());
1393 void gfxUserFontSet::UserFontCache::Dump() {
1398 printf("userfontcache dump count: %d ========\n", sUserFonts
->Count());
1399 for (auto it
= sUserFonts
->Iter(); !it
.Done(); it
.Next()) {
1402 printf("userfontcache dump ==================\n");