Bug 1828770 - Avoid using the term sane in gfx/thebes r=gfx-reviewers,bradwerth
[gecko.git] / gfx / thebes / gfxUserFontSet.cpp
blob1d83a349777466628e3c3d84c757b2b077d03c8d
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");
30 return sLog;
33 #define LOG(args) \
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),
48 mLoader(nullptr) {
49 mIsUserFontContainer = true;
50 mSrcList = std::move(aFontFaceSrcList);
51 mCurrentSrcIndex = 0;
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
120 return nullptr;
123 class MOZ_STACK_CLASS gfxOTSMessageContext : public gfxOTSContext {
124 public:
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 {
131 va_list va;
132 va_start(va, format);
134 nsCString msg;
135 msg.AppendVprintf(format, va);
137 va_end(va);
139 if (level > 0) {
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)) {
143 return;
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();
154 return ok;
157 nsTArray<gfxUserFontEntry::OTSMessage>&& TakeMessages() {
158 return std::move(mMessages);
161 private:
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& aSanitaryLength,
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);
175 if (!lengthHint) {
176 aSanitaryLength = 0;
177 return nullptr;
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.
185 aSanitaryLength = 0;
186 return nullptr;
189 aSanitaryLength = 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;
208 break;
209 case gfxFontFaceSrc::eSourceType_URL:
210 userFontData->mURI = src.mURI;
211 userFontData->mPrincipal = mPrincipal;
212 break;
213 case gfxFontFaceSrc::eSourceType_Buffer:
214 userFontData->mIsBuffer = true;
215 break;
217 userFontData->mPrivate = aPrivate;
218 userFontData->mTechFlags = src.mTechFlags;
219 userFontData->mFormatHint = src.mFormatHint;
220 userFontData->mRealName = aOriginalName;
221 if (aMetadata) {
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.
236 /*virtual*/
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,
254 nsACString& aURI) {
255 aFamilyName = mFamilyName;
257 aURI.Truncate();
258 if (aSrcIndex >= mSrcList.Length()) {
259 aURI.AppendLiteral("(end of source list)");
260 } else {
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));
276 } else {
277 aURI.AppendLiteral("(invalid URI)");
282 struct WOFFHeader {
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;
298 struct WOFF2Header {
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)) {
326 return;
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) {
332 return;
334 if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
335 return;
337 if (!aMetadata->SetLength(woff->metaCompLen, fallible)) {
338 return;
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;
357 } else {
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
361 mCurrentSrcIndex++;
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);
373 DoLoadNextSrc(true);
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(),
400 mFamilyName.get()));
401 mFontDataLoadingState = LOADING_FAILED;
402 SetLoadState(STATUS_FAILED);
403 return;
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(),
422 SlantStyle());
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();
434 if (fe) {
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);
456 return;
457 } else {
458 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
459 fontSet.get(), mCurrentSrcIndex, currSrc.mLocalName.get(),
460 mFamilyName.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);
477 return;
481 // see if we have an existing entry for this source
482 gfxFontEntry* fe =
483 gfxUserFontSet::UserFontCache::GetFont(currSrc, *this);
484 if (fe) {
485 mPlatformFontEntry = fe;
486 SetLoadState(STATUS_LOADED);
487 if (LOG_ENABLED()) {
488 LOG(
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()));
494 return;
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);
502 return;
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
516 nsresult rv =
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);
524 return;
525 } else {
526 fontSet->LogMessage(this, mCurrentSrcIndex, "font load failed",
527 nsIScriptError::errorFlag, rv);
530 } else {
531 // otherwise load font async
532 nsresult rv = fontSet->StartLoad(this, mCurrentSrcIndex);
533 bool loadOK = NS_SUCCEEDED(rv);
535 if (loadOK) {
536 if (LOG_ENABLED()) {
537 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
538 fontSet.get(), mCurrentSrcIndex,
539 currSrc.mURI->GetSpecOrDefault().get(), mFamilyName.get()));
541 return;
542 } else {
543 fontSet->LogMessage(this, mCurrentSrcIndex,
544 "failed to start download",
545 nsIScriptError::errorFlag, rv);
548 } else {
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
557 else {
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);
565 if (buffer &&
566 LoadPlatformFontSync(mCurrentSrcIndex, buffer, bufferLength)) {
567 // LoadPlatformFontSync takes ownership of the buffer, so no need
568 // to free it here.
569 SetLoadState(STATUS_LOADED);
570 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
571 currSrc.mSourceType + 1);
572 return;
573 } else {
574 fontSet->LogMessage(this, mCurrentSrcIndex, "font load failed",
575 nsIScriptError::errorFlag);
579 mCurrentSrcIndex++;
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(),
589 mFamilyName.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,
602 uint32_t aLength) {
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.
615 uint32_t sanitaryLen;
616 gfxUserFontType fontType;
617 nsTArray<OTSMessage> messages;
618 const uint8_t* sanitaryData =
619 SanitizeOpenTypeData(aFontData, aLength, sanitaryLen, fontType, messages);
621 return LoadPlatformFont(aSrcIndex, aFontData, aLength, fontType, sanitaryData,
622 sanitaryLen, 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());
630 uint32_t sanitaryLen;
631 gfxUserFontType fontType;
632 nsTArray<OTSMessage> messages;
633 const uint8_t* sanitaryData =
634 SanitizeOpenTypeData(aFontData, aLength, sanitaryLen, 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, sanitaryData, sanitaryLen,
643 std::move(messages), aCallback);
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);
658 return false;
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");
669 } else {
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,
707 originalFullName);
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,
721 aSanitizedLength);
722 if (!fe) {
723 fontSet->LogMessage(this, aSrcIndex, "not usable by platform");
727 if (fe) {
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);
759 if (LOG_ENABLED()) {
760 LOG((
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);
770 } else {
771 if (LOG_ENABLED()) {
772 LOG(
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(),
777 mFamilyName.get()));
781 // The downloaded data can now be discarded; the font entry is using the
782 // sanitized copy
783 free((void*)aOriginalFontData);
785 return fe != nullptr;
788 void gfxUserFontEntry::Load() {
789 if (mUserFontLoadState == STATUS_NOT_LOADED) {
790 LoadNextSrc();
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
812 mLoader = nullptr;
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);
819 } else {
820 bool loaded = LoadPlatformFontSync(aSrcIndex, aFontData, aLength);
821 aFontData = nullptr;
822 if (loaded) {
823 IncrementGeneration();
824 aCallback->FontLoadComplete();
825 } else {
826 FontLoadFailed(aCallback);
829 return;
832 RefPtr<gfxUserFontSet> fontSet = GetUserFontSet();
833 if (fontSet) {
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);
839 } else {
840 fontSet->LogMessage(this, aSrcIndex, "download failed",
841 nsIScriptError::errorFlag, aDownloadStatus);
845 if (aFontData) {
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",
857 aCallback));
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;
895 if (loaded) {
896 IncrementGeneration();
897 aCallback->FontLoadComplete();
898 } else {
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);
916 } else {
917 LoadNextSrc();
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) {
930 aResult.Clear();
931 RefPtr<gfxUserFontSet> fontSet = GetUserFontSet();
932 if (fontSet) {
933 aResult.AppendElement(std::move(fontSet));
937 gfxUserFontSet::gfxUserFontSet()
938 : mFontFamilies(4),
939 mRebuildGeneration(0),
940 mLocalRulesUsed(false),
941 mRebuildLocalRules(false),
942 mDownloadCount(0),
943 mDownloadSize(0) {
944 IncrementGeneration(true);
945 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
946 if (fp) {
947 fp->AddUserFontSet(this);
951 gfxUserFontSet::~gfxUserFontSet() { Destroy(); }
953 void gfxUserFontSet::Destroy() {
954 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
955 if (fp) {
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);
974 if (family) {
975 entry = FindExistingUserFontEntry(family, aFontFaceSrcList, aAttr);
978 if (!entry) {
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) {
989 aFamily->ReadLock();
990 const auto& fontList = aFamily->GetFontList();
991 gfxUserFontEntry* result = nullptr;
993 for (const auto& font : fontList) {
994 if (!font->mIsUserFontContainer) {
995 continue;
998 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(font.get());
999 if (ufe->Matches(aFontFaceSrcList, aAttr)) {
1000 result = ufe;
1001 break;
1004 aFamily->ReadUnlock();
1006 return result;
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);
1019 LOG(
1020 ("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
1021 "stretch: %s display: %d",
1022 this, aFamilyName.get(), aUserFontEntry,
1023 (aUserFontEntry->IsItalic()
1024 ? "italic"
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
1033 do {
1034 mGeneration = ++sFontSetGeneration;
1035 } while (mGeneration == 0);
1036 if (aIsRebuild) {
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);
1051 ToLowerCase(key);
1053 return mFontFamilies.Get(key);
1056 already_AddRefed<gfxUserFontFamily> gfxUserFontSet::GetFamily(
1057 const nsACString& aFamilyName) {
1058 nsAutoCString key(aFamilyName);
1059 ToLowerCase(key);
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)
1110 NS_IMETHODIMP
1111 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
1112 const char* aTopic,
1113 const char16_t* aData) {
1114 if (!sUserFonts) {
1115 return NS_OK;
1118 if (!strcmp(aTopic, "cacheservice:empty-cache")) {
1119 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1120 i.Remove();
1122 } else if (!strcmp(aTopic, "last-pb-context-exited")) {
1123 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1124 if (i.Get()->IsPrivate()) {
1125 i.Remove();
1128 } else if (!strcmp(aTopic, "xpcom-shutdown")) {
1129 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1130 i.Get()->GetFontEntry()->DisconnectSVG();
1132 } else {
1133 MOZ_ASSERT_UNREACHABLE("unexpected topic");
1136 return NS_OK;
1139 bool gfxUserFontSet::UserFontCache::Entry::KeyEquals(
1140 const KeyTypePointer aKey) const {
1141 const gfxFontEntry* fe = aKey->mFontEntry;
1143 if (!mURI->Equals(aKey->mURI)) {
1144 return false;
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)) {
1152 return false;
1156 if (mPrivate != aKey->mPrivate) {
1157 return false;
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) {
1172 return false;
1175 return true;
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")) {
1184 return;
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",
1191 aFontEntry);
1192 #endif
1193 return;
1196 if (!sUserFonts) {
1197 sUserFonts = new nsTHashtable<Entry>;
1199 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1200 if (obs) {
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
1211 // the cache.
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;
1221 } else {
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);
1228 Dump();
1229 #endif
1232 void gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry) {
1233 if (!sUserFonts) {
1234 // if we've already deleted the cache (i.e. during shutdown),
1235 // just ignore this
1236 return;
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) {
1244 i.Remove();
1248 #ifdef DEBUG_USERFONT_CACHE
1249 printf("userfontcache removed fontentry: %p\n", aFontEntry);
1250 Dump();
1251 #endif
1254 gfxFontEntry* gfxUserFontSet::UserFontCache::GetFont(
1255 const gfxFontFaceSrc& aSrc, const gfxUserFontEntry& aUserFontEntry) {
1256 if (!sUserFonts ||
1257 Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1258 return nullptr;
1261 RefPtr<gfxUserFontSet> srcFontSet = aUserFontEntry.GetUserFontSet();
1262 if (NS_WARN_IF(!srcFontSet) || srcFontSet->BypassCache()) {
1263 return nullptr;
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()));
1273 if (!entry) {
1274 return nullptr;
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)) {
1281 return nullptr;
1284 return entry->GetFontEntry();
1287 void gfxUserFontSet::UserFontCache::Shutdown() {
1288 if (sUserFonts) {
1289 delete sUserFonts;
1290 sUserFonts = nullptr;
1294 MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
1296 void gfxUserFontSet::UserFontCache::Entry::ReportMemory(
1297 nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1298 bool aAnonymize) {
1299 MOZ_ASSERT(mFontEntry);
1300 nsAutoCString path("explicit/gfx/user-fonts/font(");
1302 if (aAnonymize) {
1303 path.AppendPrintf("<anonymized-%p>", this);
1304 } else {
1305 path.AppendPrintf("family=%s", mFontEntry->mFamilyName.get());
1306 if (mURI) {
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) {
1312 spec.Truncate(252);
1313 spec.AppendLiteral("...");
1315 path.AppendPrintf(", url=%s", spec.get());
1317 if (mPrincipal) {
1318 nsAutoCString spec;
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());
1330 path.Append(')');
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,
1339 nsIMemoryReporter)
1341 NS_IMETHODIMP
1342 gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
1343 nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1344 bool aAnonymize) {
1345 if (!sUserFonts) {
1346 return NS_OK;
1349 for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1350 it.Get()->ReportMemory(aHandleReport, aData, aAnonymize);
1353 MOZ_COLLECT_REPORT(
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 "
1357 "resources.");
1359 return NS_OK;
1362 #ifdef DEBUG_USERFONT_CACHE
1364 void gfxUserFontSet::UserFontCache::Entry::Dump() {
1365 nsresult rv;
1367 nsAutoCString principalURISpec("(null)");
1368 bool setDomain = false;
1370 if (mPrincipal) {
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));
1379 if (domainURI) {
1380 setDomain = true;
1384 NS_ASSERTION(mURI, "null URI in userfont cache entry");
1386 printf(
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() {
1394 if (!sUserFonts) {
1395 return;
1398 printf("userfontcache dump count: %d ========\n", sUserFonts->Count());
1399 for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1400 it.Get()->Dump();
1402 printf("userfontcache dump ==================\n");
1405 #endif
1407 #undef LOG
1408 #undef LOG_ENABLED