Bug 1492908 [wpt PR 13122] - Update wpt metadata, a=testonly
[gecko.git] / gfx / thebes / gfxUserFontSet.cpp
blob023c7fa67b42b01a9a31c0a155d2dfac53a8bb5d
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "gfxPrefs.h"
11 #include "nsIProtocolHandler.h"
12 #include "gfxFontConstants.h"
13 #include "mozilla/FontPropertyTypes.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/Services.h"
16 #include "mozilla/Telemetry.h"
17 #include "mozilla/gfx/2D.h"
18 #include "gfxPlatformFontList.h"
19 #include "mozilla/ServoStyleSet.h"
20 #include "mozilla/PostTraversalTask.h"
22 #include "opentype-sanitiser.h"
23 #include "ots-memory-stream.h"
25 using namespace mozilla;
27 mozilla::LogModule*
28 gfxUserFontSet::GetUserFontsLog()
30 static LazyLogModule sLog("userfonts");
31 return sLog;
34 #define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
35 #define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug)
37 static uint64_t sFontSetGeneration = 0;
39 // Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
40 // adapted to use Mozilla allocators and to allow the final
41 // memory buffer to be adopted by the client.
42 class ExpandingMemoryStream : public ots::OTSStream {
43 public:
44 ExpandingMemoryStream(size_t initial, size_t limit)
45 : mLength(initial), mLimit(limit), mOff(0) {
46 mPtr = moz_xmalloc(mLength);
49 ~ExpandingMemoryStream() {
50 free(mPtr);
53 // Return the buffer, resized to fit its contents (as it may have been
54 // over-allocated during growth), and give up ownership of it so the
55 // caller becomes responsible to call free() when finished with it.
56 void* forget() {
57 void* p = moz_xrealloc(mPtr, mOff);
58 mPtr = nullptr;
59 return p;
62 bool WriteRaw(const void* data, size_t length) override {
63 if ((mOff + length > mLength) ||
64 (mLength > std::numeric_limits<size_t>::max() - mOff)) {
65 if (mLength == mLimit) {
66 return false;
68 size_t newLength = (mLength + 1) * 2;
69 if (newLength < mLength) {
70 return false;
72 if (newLength > mLimit) {
73 newLength = mLimit;
75 mPtr = moz_xrealloc(mPtr, newLength);
76 mLength = newLength;
77 return WriteRaw(data, length);
79 std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
80 mOff += length;
81 return true;
84 bool Seek(off_t position) override {
85 if (position < 0) {
86 return false;
88 if (static_cast<size_t>(position) > mLength) {
89 return false;
91 mOff = position;
92 return true;
95 off_t Tell() const override {
96 return mOff;
99 private:
100 void* mPtr;
101 size_t mLength;
102 const size_t mLimit;
103 off_t mOff;
106 gfxUserFontEntry::gfxUserFontEntry(gfxUserFontSet* aFontSet,
107 const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
108 WeightRange aWeight,
109 StretchRange aStretch,
110 SlantStyleRange aStyle,
111 const nsTArray<gfxFontFeature>& aFeatureSettings,
112 const nsTArray<gfxFontVariation>& aVariationSettings,
113 uint32_t aLanguageOverride,
114 gfxCharacterMap* aUnicodeRanges,
115 uint8_t aFontDisplay,
116 RangeFlags aRangeFlags)
117 : gfxFontEntry(NS_LITERAL_CSTRING("userfont")),
118 mUserFontLoadState(STATUS_NOT_LOADED),
119 mFontDataLoadingState(NOT_LOADING),
120 mUnsupportedFormat(false),
121 mFontDisplay(aFontDisplay),
122 mLoader(nullptr),
123 mFontSet(aFontSet)
125 mIsUserFontContainer = true;
126 mSrcList = aFontFaceSrcList;
127 mSrcIndex = 0;
128 mWeightRange = aWeight;
129 mStretchRange = aStretch;
130 mStyleRange = aStyle;
131 mFeatureSettings.AppendElements(aFeatureSettings);
132 mVariationSettings.AppendElements(aVariationSettings);
133 mLanguageOverride = aLanguageOverride;
134 mCharacterMap = aUnicodeRanges;
135 mRangeFlags = aRangeFlags;
138 gfxUserFontEntry::~gfxUserFontEntry()
140 // Assert that we don't drop any gfxUserFontEntry objects during a Servo
141 // traversal, since PostTraversalTask objects can hold raw pointers to
142 // gfxUserFontEntry objects.
143 MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
146 bool
147 gfxUserFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
148 WeightRange aWeight,
149 StretchRange aStretch,
150 SlantStyleRange aStyle,
151 const nsTArray<gfxFontFeature>& aFeatureSettings,
152 const nsTArray<gfxFontVariation>& aVariationSettings,
153 uint32_t aLanguageOverride,
154 gfxCharacterMap* aUnicodeRanges,
155 uint8_t aFontDisplay,
156 RangeFlags aRangeFlags)
158 return Weight() == aWeight &&
159 Stretch() == aStretch &&
160 SlantStyle() == aStyle &&
161 mFeatureSettings == aFeatureSettings &&
162 mVariationSettings == aVariationSettings &&
163 mLanguageOverride == aLanguageOverride &&
164 mSrcList == aFontFaceSrcList &&
165 mFontDisplay == aFontDisplay &&
166 mRangeFlags == aRangeFlags &&
167 ((!aUnicodeRanges && !mCharacterMap) ||
168 (aUnicodeRanges && mCharacterMap && mCharacterMap->Equals(aUnicodeRanges)));
171 gfxFont*
172 gfxUserFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle)
174 MOZ_ASSERT_UNREACHABLE("should only be creating a gfxFont"
175 " with an actual platform font entry");
177 // userfont entry is a container, can't create font from the container
178 return nullptr;
181 class MOZ_STACK_CLASS gfxOTSContext : public ots::OTSContext {
182 public:
183 explicit gfxOTSContext(gfxUserFontEntry* aUserFontEntry)
184 : mUserFontEntry(aUserFontEntry)
186 // Whether to apply OTS validation to OpenType Layout tables
187 mCheckOTLTables = gfxPrefs::ValidateOTLTables();
188 // Whether to preserve Variation tables in downloaded fonts
189 mCheckVariationTables = gfxPrefs::ValidateVariationTables();
190 // Whether to preserve color bitmap glyphs
191 mKeepColorBitmaps = gfxPrefs::KeepColorBitmaps();
194 virtual ots::TableAction GetTableAction(uint32_t aTag) override {
195 // Preserve Graphite, color glyph and SVG tables,
196 // and possibly OTL and Variation tables (depending on prefs)
197 if ((!mCheckOTLTables &&
198 (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') ||
199 aTag == TRUETYPE_TAG('G', 'P', 'O', 'S') ||
200 aTag == TRUETYPE_TAG('G', 'S', 'U', 'B'))) ||
201 (!mCheckVariationTables &&
202 (aTag == TRUETYPE_TAG('a', 'v', 'a', 'r') ||
203 aTag == TRUETYPE_TAG('c', 'v', 'a', 'r') ||
204 aTag == TRUETYPE_TAG('f', 'v', 'a', 'r') ||
205 aTag == TRUETYPE_TAG('g', 'v', 'a', 'r') ||
206 aTag == TRUETYPE_TAG('H', 'V', 'A', 'R') ||
207 aTag == TRUETYPE_TAG('M', 'V', 'A', 'R') ||
208 aTag == TRUETYPE_TAG('S', 'T', 'A', 'T') ||
209 aTag == TRUETYPE_TAG('V', 'V', 'A', 'R'))) ||
210 aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
211 aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
212 aTag == TRUETYPE_TAG('C', 'P', 'A', 'L') ||
213 (mKeepColorBitmaps &&
214 (aTag == TRUETYPE_TAG('C', 'B', 'D', 'T') ||
215 aTag == TRUETYPE_TAG('C', 'B', 'L', 'C'))) ||
216 false) {
217 return ots::TABLE_ACTION_PASSTHRU;
219 return ots::TABLE_ACTION_DEFAULT;
222 virtual void Message(int level, const char* format,
223 ...) MSGFUNC_FMT_ATTR override {
224 va_list va;
225 va_start(va, format);
227 nsCString msg;
228 msg.AppendPrintf(format, va);
230 va_end(va);
232 if (level > 0) {
233 // For warnings (rather than errors that cause the font to fail),
234 // we only report the first instance of any given message.
235 if (mWarningsIssued.Contains(msg)) {
236 return;
238 mWarningsIssued.PutEntry(msg);
241 mUserFontEntry->mFontSet->LogMessage(mUserFontEntry, msg.get());
244 private:
245 gfxUserFontEntry* mUserFontEntry;
246 nsTHashtable<nsCStringHashKey> mWarningsIssued;
247 bool mCheckOTLTables;
248 bool mCheckVariationTables;
249 bool mKeepColorBitmaps;
252 // Call the OTS library to sanitize an sfnt before attempting to use it.
253 // Returns a newly-allocated block, or nullptr in case of fatal errors.
254 const uint8_t*
255 gfxUserFontEntry::SanitizeOpenTypeData(const uint8_t* aData,
256 uint32_t aLength,
257 uint32_t& aSaneLength,
258 gfxUserFontType aFontType)
260 if (aFontType == GFX_USERFONT_UNKNOWN) {
261 aSaneLength = 0;
262 return nullptr;
265 uint32_t lengthHint = aLength;
266 if (aFontType == GFX_USERFONT_WOFF) {
267 lengthHint *= 2;
268 } else if (aFontType == GFX_USERFONT_WOFF2) {
269 lengthHint *= 3;
272 // limit output/expansion to 256MB
273 ExpandingMemoryStream output(lengthHint, 1024 * 1024 * 256);
275 gfxOTSContext otsContext(this);
276 if (!otsContext.Process(&output, aData, aLength)) {
277 // Failed to decode/sanitize the font, so discard it.
278 aSaneLength = 0;
279 return nullptr;
282 aSaneLength = output.Tell();
283 return static_cast<const uint8_t*>(output.forget());
286 void
287 gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
288 bool aPrivate,
289 const nsACString& aOriginalName,
290 FallibleTArray<uint8_t>* aMetadata,
291 uint32_t aMetaOrigLen,
292 uint8_t aCompression)
294 if (!aFontEntry->mUserFontData) {
295 aFontEntry->mUserFontData = MakeUnique<gfxUserFontData>();
297 gfxUserFontData* userFontData = aFontEntry->mUserFontData.get();
298 userFontData->mSrcIndex = mSrcIndex;
299 const gfxFontFaceSrc& src = mSrcList[mSrcIndex];
300 switch (src.mSourceType) {
301 case gfxFontFaceSrc::eSourceType_Local:
302 userFontData->mLocalName = src.mLocalName;
303 break;
304 case gfxFontFaceSrc::eSourceType_URL:
305 userFontData->mURI = src.mURI;
306 userFontData->mPrincipal = mPrincipal;
307 break;
308 case gfxFontFaceSrc::eSourceType_Buffer:
309 userFontData->mIsBuffer = true;
310 break;
312 userFontData->mPrivate = aPrivate;
313 userFontData->mFormat = src.mFormatFlags;
314 userFontData->mRealName = aOriginalName;
315 if (aMetadata) {
316 userFontData->mMetadata.SwapElements(*aMetadata);
317 userFontData->mMetaOrigLen = aMetaOrigLen;
318 userFontData->mCompression = aCompression;
322 size_t
323 gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
325 return aMallocSizeOf(this)
326 + mMetadata.ShallowSizeOfExcludingThis(aMallocSizeOf)
327 + mLocalName.SizeOfExcludingThisIfUnshared(aMallocSizeOf)
328 + mRealName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
329 // Not counting mURI and mPrincipal, as those will be shared.
332 /*virtual*/
333 gfxUserFontFamily::~gfxUserFontFamily()
335 // Should not be dropped by stylo
336 MOZ_ASSERT(NS_IsMainThread());
339 gfxFontSrcPrincipal*
340 gfxFontFaceSrc::LoadPrincipal(const gfxUserFontSet& aFontSet) const
342 MOZ_ASSERT(mSourceType == eSourceType_URL);
343 if (mUseOriginPrincipal && mOriginPrincipal) {
344 return mOriginPrincipal;
346 return aFontSet.GetStandardFontLoadPrincipal();
349 void
350 gfxUserFontEntry::GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
351 nsACString& aURI)
353 aFamilyName = mFamilyName;
355 aURI.Truncate();
356 if (mSrcIndex == mSrcList.Length()) {
357 aURI.AppendLiteral("(end of source list)");
358 } else {
359 if (mSrcList[mSrcIndex].mURI) {
360 mSrcList[mSrcIndex].mURI->GetSpec(aURI);
361 // If the source URI was very long, elide the middle of it.
362 // In principle, the byte-oriented chopping here could leave us
363 // with partial UTF-8 characters at the point where we cut it,
364 // but it really doesn't matter as this is just for logging.
365 const uint32_t kMaxURILengthForLogging = 256;
366 // UTF-8 ellipsis, with spaces to allow additional wrap opportunities
367 // in the resulting log message
368 const char kEllipsis[] = { ' ', '\xE2', '\x80', '\xA6', ' ' };
369 if (aURI.Length() > kMaxURILengthForLogging) {
370 aURI.Replace(kMaxURILengthForLogging / 2,
371 aURI.Length() - kMaxURILengthForLogging,
372 kEllipsis, ArrayLength(kEllipsis));
374 } else {
375 aURI.AppendLiteral("(invalid URI)");
380 struct WOFFHeader {
381 AutoSwap_PRUint32 signature;
382 AutoSwap_PRUint32 flavor;
383 AutoSwap_PRUint32 length;
384 AutoSwap_PRUint16 numTables;
385 AutoSwap_PRUint16 reserved;
386 AutoSwap_PRUint32 totalSfntSize;
387 AutoSwap_PRUint16 majorVersion;
388 AutoSwap_PRUint16 minorVersion;
389 AutoSwap_PRUint32 metaOffset;
390 AutoSwap_PRUint32 metaCompLen;
391 AutoSwap_PRUint32 metaOrigLen;
392 AutoSwap_PRUint32 privOffset;
393 AutoSwap_PRUint32 privLen;
396 struct WOFF2Header {
397 AutoSwap_PRUint32 signature;
398 AutoSwap_PRUint32 flavor;
399 AutoSwap_PRUint32 length;
400 AutoSwap_PRUint16 numTables;
401 AutoSwap_PRUint16 reserved;
402 AutoSwap_PRUint32 totalSfntSize;
403 AutoSwap_PRUint32 totalCompressedSize;
404 AutoSwap_PRUint16 majorVersion;
405 AutoSwap_PRUint16 minorVersion;
406 AutoSwap_PRUint32 metaOffset;
407 AutoSwap_PRUint32 metaCompLen;
408 AutoSwap_PRUint32 metaOrigLen;
409 AutoSwap_PRUint32 privOffset;
410 AutoSwap_PRUint32 privLen;
413 template<typename HeaderT>
414 void
415 CopyWOFFMetadata(const uint8_t* aFontData,
416 uint32_t aLength,
417 FallibleTArray<uint8_t>* aMetadata,
418 uint32_t* aMetaOrigLen)
420 // This function may be called with arbitrary, unvalidated "font" data
421 // from @font-face, so it needs to be careful to bounds-check, etc.,
422 // before trying to read anything.
423 // This just saves a copy of the compressed data block; it does NOT check
424 // that the block can be successfully decompressed, or that it contains
425 // well-formed/valid XML metadata.
426 if (aLength < sizeof(HeaderT)) {
427 return;
429 const HeaderT* woff =
430 reinterpret_cast<const HeaderT*>(aFontData);
431 uint32_t metaOffset = woff->metaOffset;
432 uint32_t metaCompLen = woff->metaCompLen;
433 if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
434 return;
436 if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
437 return;
439 if (!aMetadata->SetLength(woff->metaCompLen, fallible)) {
440 return;
442 memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
443 *aMetaOrigLen = woff->metaOrigLen;
446 void
447 gfxUserFontEntry::LoadNextSrc()
449 NS_ASSERTION(mSrcIndex < mSrcList.Length(),
450 "already at the end of the src list for user font");
451 NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
452 mUserFontLoadState == STATUS_LOAD_PENDING ||
453 mUserFontLoadState == STATUS_LOADING) &&
454 mFontDataLoadingState < LOADING_FAILED,
455 "attempting to load a font that has either completed or failed");
457 if (mUserFontLoadState == STATUS_NOT_LOADED) {
458 SetLoadState(STATUS_LOADING);
459 mFontDataLoadingState = LOADING_STARTED;
460 mUnsupportedFormat = false;
461 } else {
462 // we were already loading; move to the next source,
463 // but don't reset state - if we've already timed out,
464 // that counts against the new download
465 mSrcIndex++;
468 DoLoadNextSrc(false);
471 void
472 gfxUserFontEntry::ContinueLoad()
474 MOZ_ASSERT(mUserFontLoadState == STATUS_LOAD_PENDING);
475 MOZ_ASSERT(mSrcList[mSrcIndex].mSourceType == gfxFontFaceSrc::eSourceType_URL);
477 SetLoadState(STATUS_LOADING);
478 DoLoadNextSrc(true);
479 if (LoadState() != STATUS_LOADING) {
480 MOZ_ASSERT(mUserFontLoadState != STATUS_LOAD_PENDING,
481 "Not in parallel traversal, shouldn't get LOAD_PENDING again");
482 // Loading is synchronously finished (loaded from cache or failed). We
483 // need to increment the generation so that we flush the style data to
484 // use the new loaded font face.
485 // Without parallel traversal, we would simply get the right font data
486 // after the first call to DoLoadNextSrc() in this case, so we don't need
487 // to touch the generation to trigger another restyle.
488 // XXX We may want to return synchronously in parallel traversal in those
489 // cases as well if possible, so that we don't have an additional restyle.
490 // That doesn't work currently because nsIDocument::GetDocShell (called
491 // from FontFaceSet::CheckFontLoad) dereferences a weak pointer, which is
492 // not allowed in parallel traversal.
493 IncrementGeneration();
497 static bool
498 IgnorePrincipal(gfxFontSrcURI* aURI)
500 return aURI->InheritsSecurityContext();
503 void
504 gfxUserFontEntry::DoLoadNextSrc(bool aForceAsync)
506 uint32_t numSrc = mSrcList.Length();
508 // load each src entry in turn, until a local face is found
509 // or a download begins successfully
510 while (mSrcIndex < numSrc) {
511 gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
513 // src local ==> lookup and load immediately
515 if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
516 // Don't look up local fonts if the font whitelist is being used.
517 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
518 gfxFontEntry* fe = pfl && pfl->IsFontFamilyWhitelistActive() ?
519 nullptr :
520 gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
521 Weight(),
522 Stretch(),
523 SlantStyle());
524 nsTArray<gfxUserFontSet*> fontSets;
525 GetUserFontSets(fontSets);
526 for (gfxUserFontSet* fontSet : fontSets) {
527 // We need to note on each gfxUserFontSet that contains the user
528 // font entry that we used a local() rule.
529 fontSet->SetLocalRulesUsed();
531 if (fe) {
532 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
533 mFontSet, mSrcIndex,
534 currSrc.mLocalName.get(),
535 mFamilyName.get(),
536 uint32_t(mFontSet->mGeneration)));
537 fe->mFeatureSettings.AppendElements(mFeatureSettings);
538 fe->mVariationSettings.AppendElements(mVariationSettings);
539 fe->mLanguageOverride = mLanguageOverride;
540 fe->mFamilyName = mFamilyName;
541 fe->mRangeFlags = mRangeFlags;
542 // For src:local(), we don't care whether the request is from
543 // a private window as there's no issue of caching resources;
544 // local fonts are just available all the time.
545 StoreUserFontData(fe, false, nsCString(), nullptr, 0,
546 gfxUserFontData::kUnknownCompression);
547 mPlatformFontEntry = fe;
548 SetLoadState(STATUS_LOADED);
549 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
550 currSrc.mSourceType + 1);
551 return;
552 } else {
553 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
554 mFontSet, mSrcIndex,
555 currSrc.mLocalName.get(),
556 mFamilyName.get()));
560 // src url ==> start the load process
561 else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
562 if (gfxPlatform::GetPlatform()->IsFontFormatSupported(
563 currSrc.mFormatFlags)) {
565 if (ServoStyleSet* set = ServoStyleSet::Current()) {
566 // Only support style worker threads synchronously getting
567 // entries from the font cache when it's not a data: URI
568 // @font-face that came from UA or user sheets, since we
569 // were not able to call IsFontLoadAllowed ahead of time
570 // for these entries.
571 if (currSrc.mUseOriginPrincipal && IgnorePrincipal(currSrc.mURI)) {
572 set->AppendTask(PostTraversalTask::LoadFontEntry(this));
573 SetLoadState(STATUS_LOAD_PENDING);
574 return;
578 // see if we have an existing entry for this source
579 gfxFontEntry* fe =
580 gfxUserFontSet::UserFontCache::GetFont(currSrc, *this);
581 if (fe) {
582 mPlatformFontEntry = fe;
583 SetLoadState(STATUS_LOADED);
584 if (LOG_ENABLED()) {
585 LOG(("userfonts (%p) [src %d] "
586 "loaded uri from cache: (%s) for (%s)\n",
587 mFontSet, mSrcIndex,
588 currSrc.mURI->GetSpecOrDefault().get(),
589 mFamilyName.get()));
591 return;
594 if (ServoStyleSet* set = ServoStyleSet::Current()) {
595 // If we need to start a font load and we're on a style
596 // worker thread, we have to defer it.
597 set->AppendTask(PostTraversalTask::LoadFontEntry(this));
598 SetLoadState(STATUS_LOAD_PENDING);
599 return;
602 // record the principal we should use for the load for use when
603 // creating a channel and when caching the loaded entry.
604 mPrincipal = currSrc.LoadPrincipal(*mFontSet);
606 bool loadDoesntSpin =
607 !aForceAsync && currSrc.mURI->SyncLoadIsOK();
609 if (loadDoesntSpin) {
610 uint8_t* buffer = nullptr;
611 uint32_t bufferLength = 0;
613 // sync load font immediately
614 nsresult rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
615 bufferLength);
617 if (NS_SUCCEEDED(rv) &&
618 LoadPlatformFont(buffer, bufferLength)) {
619 SetLoadState(STATUS_LOADED);
620 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
621 currSrc.mSourceType + 1);
622 return;
623 } else {
624 mFontSet->LogMessage(this,
625 "font load failed",
626 nsIScriptError::errorFlag,
627 rv);
630 } else {
631 // otherwise load font async
632 nsresult rv = mFontSet->StartLoad(this, &currSrc);
633 bool loadOK = NS_SUCCEEDED(rv);
635 if (loadOK) {
636 if (LOG_ENABLED()) {
637 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
638 mFontSet, mSrcIndex,
639 currSrc.mURI->GetSpecOrDefault().get(),
640 mFamilyName.get()));
642 return;
643 } else {
644 mFontSet->LogMessage(this,
645 "download failed",
646 nsIScriptError::errorFlag,
647 rv);
650 } else {
651 // We don't log a warning to the web console yet,
652 // as another source may load successfully
653 mUnsupportedFormat = true;
657 // FontFace buffer ==> load immediately
659 else {
660 MOZ_ASSERT(currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Buffer);
662 uint8_t* buffer = nullptr;
663 uint32_t bufferLength = 0;
665 // sync load font immediately
666 currSrc.mBuffer->TakeBuffer(buffer, bufferLength);
667 if (buffer && LoadPlatformFont(buffer, bufferLength)) {
668 // LoadPlatformFont takes ownership of the buffer, so no need
669 // to free it here.
670 SetLoadState(STATUS_LOADED);
671 Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
672 currSrc.mSourceType + 1);
673 return;
674 } else {
675 mFontSet->LogMessage(this,
676 "font load failed",
677 nsIScriptError::errorFlag);
681 mSrcIndex++;
684 if (mUnsupportedFormat) {
685 mFontSet->LogMessage(this, "no supported format found",
686 nsIScriptError::warningFlag);
689 // all src's failed; mark this entry as unusable (so fallback will occur)
690 LOG(("userfonts (%p) failed all src for (%s)\n",
691 mFontSet, mFamilyName.get()));
692 mFontDataLoadingState = LOADING_FAILED;
693 SetLoadState(STATUS_FAILED);
696 void
697 gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState)
699 mUserFontLoadState = aLoadState;
702 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc)
704 bool
705 gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
707 AUTO_PROFILER_LABEL("gfxUserFontEntry::LoadPlatformFont", OTHER);
708 NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
709 mUserFontLoadState == STATUS_LOAD_PENDING ||
710 mUserFontLoadState == STATUS_LOADING) &&
711 mFontDataLoadingState < LOADING_FAILED,
712 "attempting to load a font that has either completed or failed");
714 gfxFontEntry* fe = nullptr;
716 gfxUserFontType fontType =
717 gfxFontUtils::DetermineFontDataType(aFontData, aLength);
718 Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(fontType));
720 // Unwrap/decompress/sanitize or otherwise munge the downloaded data
721 // to make a usable sfnt structure.
723 // Because platform font activation code may replace the name table
724 // in the font with a synthetic one, we save the original name so that
725 // it can be reported via the InspectorUtils API.
726 nsAutoCString originalFullName;
728 // Call the OTS sanitizer; this will also decode WOFF to sfnt
729 // if necessary. The original data in aFontData is left unchanged.
730 uint32_t saneLen;
731 uint32_t fontCompressionRatio = 0;
732 size_t computedSize = 0;
733 const uint8_t* saneData =
734 SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType);
735 if (!saneData) {
736 mFontSet->LogMessage(this, "rejected by sanitizer");
737 } else {
738 // Check whether saneData is a known OpenType format; it might be
739 // a TrueType Collection, which OTS would accept but we don't yet
740 // know how to handle. If so, discard.
741 if (gfxFontUtils::DetermineFontDataType(saneData, saneLen) !=
742 GFX_USERFONT_OPENTYPE) {
743 mFontSet->LogMessage(this, "not a supported OpenType format");
744 free((void*)saneData);
745 saneData = nullptr;
748 if (saneData) {
749 if (saneLen) {
750 fontCompressionRatio = uint32_t(100.0 * aLength / saneLen + 0.5);
751 if (fontType == GFX_USERFONT_WOFF ||
752 fontType == GFX_USERFONT_WOFF2) {
753 Telemetry::Accumulate(fontType == GFX_USERFONT_WOFF ?
754 Telemetry::WEBFONT_COMPRESSION_WOFF :
755 Telemetry::WEBFONT_COMPRESSION_WOFF2,
756 fontCompressionRatio);
760 // The sanitizer ensures that we have a valid sfnt and a usable
761 // name table, so this should never fail unless we're out of
762 // memory, and GetFullNameFromSFNT is not directly exposed to
763 // arbitrary/malicious data from the web.
764 gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
765 originalFullName);
767 // Record size for memory reporting purposes. We measure this now
768 // because by the time we potentially want to collect reports, this
769 // data block may have been handed off to opaque OS font APIs that
770 // don't allow us to retrieve or measure it directly.
771 // The *OnAlloc function will also tell DMD about this block, as the
772 // OS font code may hold on to it for an extended period.
773 computedSize = UserFontMallocSizeOfOnAlloc(saneData);
775 // Here ownership of saneData is passed to the platform,
776 // which will delete it when no longer required
777 fe = gfxPlatform::GetPlatform()->MakePlatformFont(mName,
778 Weight(),
779 Stretch(),
780 SlantStyle(),
781 saneData,
782 saneLen);
783 if (!fe) {
784 mFontSet->LogMessage(this, "not usable by platform");
788 if (fe) {
789 fe->mComputedSizeOfUserFont = computedSize;
791 // Save a copy of the metadata block (if present) for InspectorUtils
792 // to use if required. Ownership of the metadata block will be passed
793 // to the gfxUserFontData record below.
794 FallibleTArray<uint8_t> metadata;
795 uint32_t metaOrigLen = 0;
796 uint8_t compression = gfxUserFontData::kUnknownCompression;
797 if (fontType == GFX_USERFONT_WOFF) {
798 CopyWOFFMetadata<WOFFHeader>(aFontData, aLength,
799 &metadata, &metaOrigLen);
800 compression = gfxUserFontData::kZlibCompression;
801 } else if (fontType == GFX_USERFONT_WOFF2) {
802 CopyWOFFMetadata<WOFF2Header>(aFontData, aLength,
803 &metadata, &metaOrigLen);
804 compression = gfxUserFontData::kBrotliCompression;
807 // copy OpenType feature/language settings from the userfont entry to the
808 // newly-created font entry
809 fe->mFeatureSettings.AppendElements(mFeatureSettings);
810 fe->mVariationSettings.AppendElements(mVariationSettings);
811 fe->mLanguageOverride = mLanguageOverride;
812 fe->mFamilyName = mFamilyName;
813 fe->mRangeFlags = mRangeFlags;
814 StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName,
815 &metadata, metaOrigLen, compression);
816 if (LOG_ENABLED()) {
817 LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
818 "(%p) gen: %8.8x compress: %d%%\n",
819 mFontSet, mSrcIndex,
820 mSrcList[mSrcIndex].mURI->GetSpecOrDefault().get(),
821 mFamilyName.get(),
822 this, uint32_t(mFontSet->mGeneration), fontCompressionRatio));
824 mPlatformFontEntry = fe;
825 SetLoadState(STATUS_LOADED);
826 gfxUserFontSet::UserFontCache::CacheFont(fe);
827 } else {
828 if (LOG_ENABLED()) {
829 LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
830 " error making platform font\n",
831 mFontSet, mSrcIndex,
832 mSrcList[mSrcIndex].mURI->GetSpecOrDefault().get(),
833 mFamilyName.get()));
837 // The downloaded data can now be discarded; the font entry is using the
838 // sanitized copy
839 free((void*)aFontData);
841 return fe != nullptr;
844 void
845 gfxUserFontEntry::Load()
847 if (mUserFontLoadState == STATUS_NOT_LOADED) {
848 LoadNextSrc();
852 void
853 gfxUserFontEntry::IncrementGeneration()
855 nsTArray<gfxUserFontSet*> fontSets;
856 GetUserFontSets(fontSets);
857 for (gfxUserFontSet* fontSet : fontSets) {
858 fontSet->IncrementGeneration();
862 // This is called when a font download finishes.
863 // Ownership of aFontData passes in here, and the font set must
864 // ensure that it is eventually deleted via free().
865 bool
866 gfxUserFontEntry::FontDataDownloadComplete(const uint8_t* aFontData,
867 uint32_t aLength,
868 nsresult aDownloadStatus)
870 // forget about the loader, as we no longer potentially need to cancel it
871 // if the entry is obsoleted
872 mLoader = nullptr;
874 // download successful, make platform font using font data
875 if (NS_SUCCEEDED(aDownloadStatus) &&
876 mFontDataLoadingState != LOADING_TIMED_OUT) {
877 bool loaded = LoadPlatformFont(aFontData, aLength);
878 aFontData = nullptr;
880 if (loaded) {
881 IncrementGeneration();
882 return true;
885 } else {
886 // download failed
887 mFontSet->LogMessage(this,
888 (mFontDataLoadingState != LOADING_TIMED_OUT ?
889 "download failed" : "download timed out"),
890 nsIScriptError::errorFlag,
891 aDownloadStatus);
894 if (aFontData) {
895 free((void*)aFontData);
898 // Error occurred. Make sure the FontFace's promise is rejected if the
899 // load timed out, or else load the next src.
900 if (mFontDataLoadingState == LOADING_TIMED_OUT) {
901 mFontDataLoadingState = LOADING_FAILED;
902 SetLoadState(STATUS_FAILED);
903 } else {
904 LoadNextSrc();
907 // We ignore the status returned by LoadNext();
908 // even if loading failed, we need to bump the font-set generation
909 // and return true in order to trigger reflow, so that fallback
910 // will be used where the text was "masked" by the pending download
911 IncrementGeneration();
912 return true;
915 void
916 gfxUserFontEntry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult)
918 aResult.Clear();
919 aResult.AppendElement(mFontSet);
922 gfxUserFontSet::gfxUserFontSet()
923 : mFontFamilies(4),
924 mRebuildGeneration(0),
925 mLocalRulesUsed(false),
926 mRebuildLocalRules(false),
927 mDownloadCount(0),
928 mDownloadSize(0)
930 IncrementGeneration(true);
931 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
932 if (fp) {
933 fp->AddUserFontSet(this);
937 gfxUserFontSet::~gfxUserFontSet()
939 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
940 if (fp) {
941 fp->RemoveUserFontSet(this);
945 already_AddRefed<gfxUserFontEntry>
946 gfxUserFontSet::FindOrCreateUserFontEntry(
947 const nsACString& aFamilyName,
948 const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
949 WeightRange aWeight,
950 StretchRange aStretch,
951 SlantStyleRange aStyle,
952 const nsTArray<gfxFontFeature>& aFeatureSettings,
953 const nsTArray<gfxFontVariation>& aVariationSettings,
954 uint32_t aLanguageOverride,
955 gfxCharacterMap* aUnicodeRanges,
956 uint8_t aFontDisplay,
957 RangeFlags aRangeFlags)
959 RefPtr<gfxUserFontEntry> entry;
961 // If there's already a userfont entry in the family whose descriptors all match,
962 // we can just move it to the end of the list instead of adding a new
963 // face that will always "shadow" the old one.
964 // Note that we can't do this for platform font entries, even if the
965 // style descriptors match, as they might have had a different source list,
966 // but we no longer have the old source list available to check.
967 gfxUserFontFamily* family = LookupFamily(aFamilyName);
968 if (family) {
969 entry = FindExistingUserFontEntry(family, aFontFaceSrcList, aWeight,
970 aStretch, aStyle,
971 aFeatureSettings, aVariationSettings,
972 aLanguageOverride,
973 aUnicodeRanges, aFontDisplay,
974 aRangeFlags);
977 if (!entry) {
978 entry = CreateUserFontEntry(aFontFaceSrcList, aWeight, aStretch,
979 aStyle, aFeatureSettings, aVariationSettings,
980 aLanguageOverride, aUnicodeRanges,
981 aFontDisplay, aRangeFlags);
982 entry->mFamilyName = aFamilyName;
985 return entry.forget();
988 gfxUserFontEntry*
989 gfxUserFontSet::FindExistingUserFontEntry(
990 gfxUserFontFamily* aFamily,
991 const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
992 WeightRange aWeight,
993 StretchRange aStretch,
994 SlantStyleRange aStyle,
995 const nsTArray<gfxFontFeature>& aFeatureSettings,
996 const nsTArray<gfxFontVariation>& aVariationSettings,
997 uint32_t aLanguageOverride,
998 gfxCharacterMap* aUnicodeRanges,
999 uint8_t aFontDisplay,
1000 RangeFlags aRangeFlags)
1002 nsTArray<RefPtr<gfxFontEntry>>& fontList = aFamily->GetFontList();
1004 for (size_t i = 0, count = fontList.Length(); i < count; i++) {
1005 if (!fontList[i]->mIsUserFontContainer) {
1006 continue;
1009 gfxUserFontEntry* existingUserFontEntry =
1010 static_cast<gfxUserFontEntry*>(fontList[i].get());
1011 if (!existingUserFontEntry->Matches(aFontFaceSrcList,
1012 aWeight, aStretch, aStyle,
1013 aFeatureSettings, aVariationSettings,
1014 aLanguageOverride,
1015 aUnicodeRanges, aFontDisplay,
1016 aRangeFlags)) {
1017 continue;
1020 return existingUserFontEntry;
1023 return nullptr;
1026 void
1027 gfxUserFontSet::AddUserFontEntry(const nsCString& aFamilyName,
1028 gfxUserFontEntry* aUserFontEntry)
1030 gfxUserFontFamily* family = GetFamily(aFamilyName);
1031 family->AddFontEntry(aUserFontEntry);
1033 if (LOG_ENABLED()) {
1034 nsAutoCString weightString;
1035 aUserFontEntry->Weight().ToString(weightString);
1036 nsAutoCString stretchString;
1037 aUserFontEntry->Stretch().ToString(stretchString);
1038 LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
1039 "stretch: %s display: %d",
1040 this, aFamilyName.get(), aUserFontEntry,
1041 (aUserFontEntry->IsItalic() ? "italic" :
1042 (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
1043 weightString.get(),
1044 stretchString.get(),
1045 aUserFontEntry->GetFontDisplay()));
1049 void
1050 gfxUserFontSet::IncrementGeneration(bool aIsRebuild)
1052 // add one, increment again if zero
1053 ++sFontSetGeneration;
1054 if (sFontSetGeneration == 0)
1055 ++sFontSetGeneration;
1056 mGeneration = sFontSetGeneration;
1057 if (aIsRebuild) {
1058 mRebuildGeneration = mGeneration;
1062 void
1063 gfxUserFontSet::RebuildLocalRules()
1065 if (mLocalRulesUsed) {
1066 mRebuildLocalRules = true;
1067 DoRebuildUserFontSet();
1071 gfxUserFontFamily*
1072 gfxUserFontSet::LookupFamily(const nsACString& aFamilyName) const
1074 nsAutoCString key(aFamilyName);
1075 ToLowerCase(key);
1077 return mFontFamilies.GetWeak(key);
1080 bool
1081 gfxUserFontSet::ContainsUserFontSetFonts(const FontFamilyList& aFontList) const
1083 for (const FontFamilyName& name : aFontList.GetFontlist()->mNames) {
1084 if (!name.IsNamed()) {
1085 continue;
1087 if (LookupFamily(nsAtomCString(name.mName))) {
1088 return true;
1091 return false;
1094 gfxUserFontFamily*
1095 gfxUserFontSet::GetFamily(const nsACString& aFamilyName)
1097 nsAutoCString key(aFamilyName);
1098 ToLowerCase(key);
1100 gfxUserFontFamily* family = mFontFamilies.GetWeak(key);
1101 if (!family) {
1102 family = new gfxUserFontFamily(aFamilyName);
1103 mFontFamilies.Put(key, family);
1105 return family;
1108 ///////////////////////////////////////////////////////////////////////////////
1109 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
1110 // across pages/fontsets rather than instantiating new platform fonts.
1112 // Entries are added to this cache when a platform font is instantiated from
1113 // downloaded data, and removed when the platform font entry is destroyed.
1114 // We don't need to use a timed expiration scheme here because the gfxFontEntry
1115 // for a downloaded font will be kept alive by its corresponding gfxFont
1116 // instance(s) until they are deleted, and *that* happens using an expiration
1117 // tracker (gfxFontCache). The result is that the downloaded font instances
1118 // recorded here will persist between pages and can get reused (provided the
1119 // source URI and principal match, of course).
1120 ///////////////////////////////////////////////////////////////////////////////
1122 nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
1123 gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
1125 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
1127 NS_IMETHODIMP
1128 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
1129 const char* aTopic,
1130 const char16_t* aData)
1132 if (!sUserFonts) {
1133 return NS_OK;
1136 if (!strcmp(aTopic, "cacheservice:empty-cache")) {
1137 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1138 i.Remove();
1140 } else if (!strcmp(aTopic, "last-pb-context-exited")) {
1141 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1142 if (i.Get()->IsPrivate()) {
1143 i.Remove();
1146 } else if (!strcmp(aTopic, "xpcom-shutdown")) {
1147 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1148 i.Get()->GetFontEntry()->DisconnectSVG();
1150 } else {
1151 MOZ_ASSERT_UNREACHABLE("unexpected topic");
1154 return NS_OK;
1157 bool
1158 gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
1160 const gfxFontEntry* fe = aKey->mFontEntry;
1162 if (!mURI->Equals(aKey->mURI)) {
1163 return false;
1166 // For data: URIs, we don't care about the principal; otherwise, check it.
1167 if (!IgnorePrincipal(mURI)) {
1168 NS_ASSERTION(mPrincipal && aKey->mPrincipal,
1169 "only data: URIs are allowed to omit the principal");
1170 if (!mPrincipal->Equals(aKey->mPrincipal)) {
1171 return false;
1175 if (mPrivate != aKey->mPrivate) {
1176 return false;
1179 if (mFontEntry->SlantStyle() != fe->SlantStyle() ||
1180 mFontEntry->Weight() != fe->Weight() ||
1181 mFontEntry->Stretch() != fe->Stretch() ||
1182 mFontEntry->mFeatureSettings != fe->mFeatureSettings ||
1183 mFontEntry->mVariationSettings != fe->mVariationSettings ||
1184 mFontEntry->mLanguageOverride != fe->mLanguageOverride ||
1185 mFontEntry->mFamilyName != fe->mFamilyName) {
1186 return false;
1189 return true;
1192 void
1193 gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry)
1195 NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
1196 "caching a font associated with no family yet");
1198 // if caching is disabled, simply return
1199 if (Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1200 return;
1203 gfxUserFontData* data = aFontEntry->mUserFontData.get();
1204 if (data->mIsBuffer) {
1205 #ifdef DEBUG_USERFONT_CACHE
1206 printf("userfontcache skipped fontentry with buffer source: %p\n",
1207 aFontEntry);
1208 #endif
1209 return;
1212 if (!sUserFonts) {
1213 sUserFonts = new nsTHashtable<Entry>;
1215 nsCOMPtr<nsIObserverService> obs =
1216 mozilla::services::GetObserverService();
1217 if (obs) {
1218 Flusher* flusher = new Flusher;
1219 obs->AddObserver(flusher, "cacheservice:empty-cache",
1220 false);
1221 obs->AddObserver(flusher, "last-pb-context-exited", false);
1222 obs->AddObserver(flusher, "xpcom-shutdown", false);
1225 // Create and register a memory reporter for sUserFonts.
1226 // This reporter is never unregistered, but that's OK because
1227 // the reporter checks whether sUserFonts is null, so it would
1228 // be safe to call even after UserFontCache::Shutdown has deleted
1229 // the cache.
1230 RegisterStrongMemoryReporter(new MemoryReporter());
1233 // For data: URIs, the principal is ignored; anyone who has the same
1234 // data: URI is able to load it and get an equivalent font.
1235 // Otherwise, the principal is used as part of the cache key.
1236 gfxFontSrcPrincipal* principal;
1237 if (IgnorePrincipal(data->mURI)) {
1238 principal = nullptr;
1239 } else {
1240 principal = data->mPrincipal;
1242 sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry,
1243 data->mPrivate));
1245 #ifdef DEBUG_USERFONT_CACHE
1246 printf("userfontcache added fontentry: %p\n", aFontEntry);
1247 Dump();
1248 #endif
1251 void
1252 gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry)
1254 if (!sUserFonts) {
1255 // if we've already deleted the cache (i.e. during shutdown),
1256 // just ignore this
1257 return;
1260 // We can't simply use RemoveEntry here because it's possible the principal
1261 // may have changed since the font was cached, in which case the lookup
1262 // would no longer find the entry (bug 838105).
1263 for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1264 if (i.Get()->GetFontEntry() == aFontEntry) {
1265 i.Remove();
1269 #ifdef DEBUG_USERFONT_CACHE
1270 printf("userfontcache removed fontentry: %p\n", aFontEntry);
1271 Dump();
1272 #endif
1275 gfxFontEntry*
1276 gfxUserFontSet::UserFontCache::GetFont(const gfxFontFaceSrc& aSrc,
1277 const gfxUserFontEntry& aUserFontEntry)
1279 if (!sUserFonts ||
1280 aUserFontEntry.mFontSet->BypassCache() ||
1281 Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1282 return nullptr;
1285 // Ignore principal when looking up a data: URI.
1286 gfxFontSrcPrincipal* principal = IgnorePrincipal(aSrc.mURI)
1287 ? nullptr
1288 : aSrc.LoadPrincipal(*aUserFontEntry.mFontSet);
1290 Entry* entry = sUserFonts->GetEntry(
1291 Key(aSrc.mURI,
1292 principal,
1293 const_cast<gfxUserFontEntry*>(&aUserFontEntry),
1294 aUserFontEntry.mFontSet->GetPrivateBrowsing()));
1295 if (!entry) {
1296 return nullptr;
1299 // We have to perform another content policy check here to prevent
1300 // cache poisoning. E.g. a.com loads a font into the cache but
1301 // b.com has a CSP not allowing any fonts to be loaded.
1302 if (!aUserFontEntry.mFontSet->IsFontLoadAllowed(aSrc)) {
1303 return nullptr;
1306 return entry->GetFontEntry();
1309 void
1310 gfxUserFontSet::UserFontCache::Shutdown()
1312 if (sUserFonts) {
1313 delete sUserFonts;
1314 sUserFonts = nullptr;
1318 MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
1320 void
1321 gfxUserFontSet::UserFontCache::Entry::ReportMemory(
1322 nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
1324 MOZ_ASSERT(mFontEntry);
1325 nsAutoCString path("explicit/gfx/user-fonts/font(");
1327 if (aAnonymize) {
1328 path.AppendPrintf("<anonymized-%p>", this);
1329 } else {
1330 path.AppendPrintf("family=%s", mFontEntry->mFamilyName.get());
1331 if (mURI) {
1332 nsCString spec = mURI->GetSpecOrDefault();
1333 spec.ReplaceChar('/', '\\');
1334 // Some fonts are loaded using horrendously-long data: URIs;
1335 // truncate those before reporting them.
1336 bool isData;
1337 if (NS_SUCCEEDED(mURI->get()->SchemeIs("data", &isData)) && isData &&
1338 spec.Length() > 255) {
1339 spec.Truncate(252);
1340 spec.AppendLiteral("...");
1342 path.AppendPrintf(", url=%s", spec.get());
1344 if (mPrincipal) {
1345 nsCOMPtr<nsIURI> uri;
1346 mPrincipal->get()->GetURI(getter_AddRefs(uri));
1347 if (uri) {
1348 nsCString spec = uri->GetSpecOrDefault();
1349 if (!spec.IsEmpty()) {
1350 // Include a clue as to who loaded this resource. (Note
1351 // that because of font entry sharing, other pages may now
1352 // be using this resource, and the original page may not
1353 // even be loaded any longer.)
1354 spec.ReplaceChar('/', '\\');
1355 path.AppendPrintf(", principal=%s", spec.get());
1360 path.Append(')');
1362 aHandleReport->Callback(
1363 EmptyCString(), path,
1364 nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
1365 mFontEntry->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf),
1366 NS_LITERAL_CSTRING("Memory used by @font-face resource."),
1367 aData);
1370 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter,
1371 nsIMemoryReporter)
1373 NS_IMETHODIMP
1374 gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
1375 nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
1377 if (!sUserFonts) {
1378 return NS_OK;
1381 for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1382 it.Get()->ReportMemory(aHandleReport, aData, aAnonymize);
1385 MOZ_COLLECT_REPORT(
1386 "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP, UNITS_BYTES,
1387 sUserFonts->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf),
1388 "Memory used by the @font-face cache, not counting the actual font "
1389 "resources.");
1391 return NS_OK;
1394 #ifdef DEBUG_USERFONT_CACHE
1396 void
1397 gfxUserFontSet::UserFontCache::Entry::Dump()
1399 nsresult rv;
1401 nsAutoCString principalURISpec("(null)");
1402 bool setDomain = false;
1404 if (mPrincipal) {
1405 nsCOMPtr<nsIURI> principalURI;
1406 rv = mPrincipal->get()->GetURI(getter_AddRefs(principalURI));
1407 if (NS_SUCCEEDED(rv)) {
1408 principalURI->GetSpec(principalURISpec);
1411 nsCOMPtr<nsIURI> domainURI;
1412 mPrincipal->get()->GetDomain(getter_AddRefs(domainURI));
1413 if (domainURI) {
1414 setDomain = true;
1418 NS_ASSERTION(mURI, "null URI in userfont cache entry");
1420 printf("userfontcache fontEntry: %p fonturihash: %8.8x "
1421 "family: %s domainset: %s principal: [%s]\n",
1422 mFontEntry,
1423 mURI->Hash(),
1424 mFontEntry->FamilyName().get(),
1425 setDomain ? "true" : "false",
1426 principalURISpec.get());
1429 void
1430 gfxUserFontSet::UserFontCache::Dump()
1432 if (!sUserFonts) {
1433 return;
1436 printf("userfontcache dump count: %d ========\n", sUserFonts->Count());
1437 for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1438 it.Get()->Dump();
1440 printf("userfontcache dump ==================\n");
1443 #endif
1445 #undef LOG
1446 #undef LOG_ENABLED