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/. */
5 
6 #include "mozilla/Logging.h"
7 
8 #include "gfxUserFontSet.h"
9 #include "gfxPlatform.h"
10 #include "gfxFontConstants.h"
11 #include "mozilla/FontPropertyTypes.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/ProfilerLabels.h"
14 #include "mozilla/Services.h"
15 #include "mozilla/StaticPrefs_gfx.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"
21 #include "gfxOTSUtils.h"
22 #include "nsIFontLoadCompleteCallback.h"
23 #include "nsProxyRelease.h"
24 #include "nsTHashSet.h"
25 
26 using namespace mozilla;
27 
GetUserFontsLog()28 mozilla::LogModule* gfxUserFontSet::GetUserFontsLog() {
29   static LazyLogModule sLog("userfonts");
30   return sLog;
31 }
32 
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)
37 
38 static uint64_t sFontSetGeneration = 0;
39 
gfxUserFontEntry(gfxUserFontSet * aFontSet,const nsTArray<gfxFontFaceSrc> & aFontFaceSrcList,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,const nsTArray<gfxFontFeature> & aFeatureSettings,const nsTArray<gfxFontVariation> & aVariationSettings,uint32_t aLanguageOverride,gfxCharacterMap * aUnicodeRanges,StyleFontDisplay aFontDisplay,RangeFlags aRangeFlags,float aAscentOverride,float aDescentOverride,float aLineGapOverride,float aSizeAdjust)40 gfxUserFontEntry::gfxUserFontEntry(
41     gfxUserFontSet* aFontSet, const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
42     WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle,
43     const nsTArray<gfxFontFeature>& aFeatureSettings,
44     const nsTArray<gfxFontVariation>& aVariationSettings,
45     uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
46     StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags,
47     float aAscentOverride, float aDescentOverride, float aLineGapOverride,
48     float aSizeAdjust)
49     : gfxFontEntry("userfont"_ns),
50       mUserFontLoadState(STATUS_NOT_LOADED),
51       mFontDataLoadingState(NOT_LOADING),
52       mSeenLocalSource(false),
53       mUnsupportedFormat(false),
54       mFontDisplay(aFontDisplay),
55       mLoader(nullptr),
56       mFontSet(aFontSet) {
57   mIsUserFontContainer = true;
58   mSrcList = aFontFaceSrcList.Clone();
59   mCurrentSrcIndex = 0;
60   mWeightRange = aWeight;
61   mStretchRange = aStretch;
62   mStyleRange = aStyle;
63   mFeatureSettings.AppendElements(aFeatureSettings);
64   mVariationSettings.AppendElements(aVariationSettings);
65   mLanguageOverride = aLanguageOverride;
66   mCharacterMap = aUnicodeRanges;
67   mRangeFlags = aRangeFlags;
68   mAscentOverride = aAscentOverride;
69   mDescentOverride = aDescentOverride;
70   mLineGapOverride = aLineGapOverride;
71   mSizeAdjust = aSizeAdjust;
72 }
73 
UpdateAttributes(WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,const nsTArray<gfxFontFeature> & aFeatureSettings,const nsTArray<gfxFontVariation> & aVariationSettings,uint32_t aLanguageOverride,gfxCharacterMap * aUnicodeRanges,StyleFontDisplay aFontDisplay,RangeFlags aRangeFlags,float aAscentOverride,float aDescentOverride,float aLineGapOverride,float aSizeAdjust)74 void gfxUserFontEntry::UpdateAttributes(
75     WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle,
76     const nsTArray<gfxFontFeature>& aFeatureSettings,
77     const nsTArray<gfxFontVariation>& aVariationSettings,
78     uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
79     StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags,
80     float aAscentOverride, float aDescentOverride, float aLineGapOverride,
81     float aSizeAdjust) {
82   // Remove the entry from the user font cache, if present there, as the cache
83   // key may no longer be correct with the new attributes.
84   gfxUserFontSet::UserFontCache::ForgetFont(this);
85 
86   mFontDisplay = aFontDisplay;
87   mWeightRange = aWeight;
88   mStretchRange = aStretch;
89   mStyleRange = aStyle;
90   mFeatureSettings = aFeatureSettings.Clone();
91   mVariationSettings = aVariationSettings.Clone();
92   mLanguageOverride = aLanguageOverride;
93   mCharacterMap = aUnicodeRanges;
94   mRangeFlags = aRangeFlags;
95   mAscentOverride = aAscentOverride;
96   mDescentOverride = aDescentOverride;
97   mLineGapOverride = aLineGapOverride;
98   mSizeAdjust = aSizeAdjust;
99 }
100 
~gfxUserFontEntry()101 gfxUserFontEntry::~gfxUserFontEntry() {
102   // Assert that we don't drop any gfxUserFontEntry objects during a Servo
103   // traversal, since PostTraversalTask objects can hold raw pointers to
104   // gfxUserFontEntry objects.
105   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
106 }
107 
Matches(const nsTArray<gfxFontFaceSrc> & aFontFaceSrcList,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,const nsTArray<gfxFontFeature> & aFeatureSettings,const nsTArray<gfxFontVariation> & aVariationSettings,uint32_t aLanguageOverride,gfxCharacterMap * aUnicodeRanges,StyleFontDisplay aFontDisplay,RangeFlags aRangeFlags,float aAscentOverride,float aDescentOverride,float aLineGapOverride,float aSizeAdjust)108 bool gfxUserFontEntry::Matches(
109     const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, WeightRange aWeight,
110     StretchRange aStretch, SlantStyleRange aStyle,
111     const nsTArray<gfxFontFeature>& aFeatureSettings,
112     const nsTArray<gfxFontVariation>& aVariationSettings,
113     uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
114     StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags,
115     float aAscentOverride, float aDescentOverride, float aLineGapOverride,
116     float aSizeAdjust) {
117   return Weight() == aWeight && Stretch() == aStretch &&
118          SlantStyle() == aStyle && mFeatureSettings == aFeatureSettings &&
119          mVariationSettings == aVariationSettings &&
120          mLanguageOverride == aLanguageOverride &&
121          mSrcList == aFontFaceSrcList && mFontDisplay == aFontDisplay &&
122          mRangeFlags == aRangeFlags && mAscentOverride == aAscentOverride &&
123          mDescentOverride == aDescentOverride &&
124          mLineGapOverride == aLineGapOverride && mSizeAdjust == aSizeAdjust &&
125          ((!aUnicodeRanges && !mCharacterMap) ||
126           (aUnicodeRanges && mCharacterMap &&
127            mCharacterMap->Equals(aUnicodeRanges)));
128 }
129 
CreateFontInstance(const gfxFontStyle * aFontStyle)130 gfxFont* gfxUserFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
131   MOZ_ASSERT_UNREACHABLE(
132       "should only be creating a gfxFont"
133       " with an actual platform font entry");
134 
135   // userfont entry is a container, can't create font from the container
136   return nullptr;
137 }
138 
139 class MOZ_STACK_CLASS gfxOTSMessageContext : public gfxOTSContext {
140  public:
~gfxOTSMessageContext()141   virtual ~gfxOTSMessageContext() {
142     MOZ_ASSERT(mMessages.IsEmpty(), "should have called TakeMessages");
143   }
144 
Message(int level,const char * format,...)145   virtual void Message(int level, const char* format,
146                        ...) MSGFUNC_FMT_ATTR override {
147     va_list va;
148     va_start(va, format);
149 
150     nsCString msg;
151     msg.AppendVprintf(format, va);
152 
153     va_end(va);
154 
155     if (level > 0) {
156       // For warnings (rather than errors that cause the font to fail),
157       // we only report the first instance of any given message.
158       if (!mWarningsIssued.EnsureInserted(msg)) {
159         return;
160       }
161     }
162 
163     mMessages.AppendElement(gfxUserFontEntry::OTSMessage{msg, level});
164   }
165 
Process(ots::OTSStream * aOutput,const uint8_t * aInput,size_t aLength,nsTArray<gfxUserFontEntry::OTSMessage> & aMessages)166   bool Process(ots::OTSStream* aOutput, const uint8_t* aInput, size_t aLength,
167                nsTArray<gfxUserFontEntry::OTSMessage>& aMessages) {
168     bool ok = ots::OTSContext::Process(aOutput, aInput, aLength);
169     aMessages = TakeMessages();
170     return ok;
171   }
172 
TakeMessages()173   nsTArray<gfxUserFontEntry::OTSMessage>&& TakeMessages() {
174     return std::move(mMessages);
175   }
176 
177  private:
178   nsTHashSet<nsCString> mWarningsIssued;
179   nsTArray<gfxUserFontEntry::OTSMessage> mMessages;
180 };
181 
182 // Call the OTS library to sanitize an sfnt before attempting to use it.
183 // Returns a newly-allocated block, or nullptr in case of fatal errors.
SanitizeOpenTypeData(const uint8_t * aData,uint32_t aLength,uint32_t & aSaneLength,gfxUserFontType & aFontType,nsTArray<OTSMessage> & aMessages)184 const uint8_t* gfxUserFontEntry::SanitizeOpenTypeData(
185     const uint8_t* aData, uint32_t aLength, uint32_t& aSaneLength,
186     gfxUserFontType& aFontType, nsTArray<OTSMessage>& aMessages) {
187   aFontType = gfxFontUtils::DetermineFontDataType(aData, aLength);
188   Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(aFontType));
189 
190   size_t lengthHint = gfxOTSContext::GuessSanitizedFontSize(aLength, aFontType);
191   if (!lengthHint) {
192     aSaneLength = 0;
193     return nullptr;
194   }
195 
196   gfxOTSExpandingMemoryStream<gfxOTSMozAlloc> output(lengthHint);
197 
198   gfxOTSMessageContext otsContext;
199   if (!otsContext.Process(&output, aData, aLength, aMessages)) {
200     // Failed to decode/sanitize the font, so discard it.
201     aSaneLength = 0;
202     return nullptr;
203   }
204 
205   aSaneLength = output.Tell();
206   return static_cast<const uint8_t*>(output.forget());
207 }
208 
StoreUserFontData(gfxFontEntry * aFontEntry,uint32_t aSrcIndex,bool aPrivate,const nsACString & aOriginalName,FallibleTArray<uint8_t> * aMetadata,uint32_t aMetaOrigLen,uint8_t aCompression)209 void gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
210                                          uint32_t aSrcIndex, bool aPrivate,
211                                          const nsACString& aOriginalName,
212                                          FallibleTArray<uint8_t>* aMetadata,
213                                          uint32_t aMetaOrigLen,
214                                          uint8_t aCompression) {
215   if (!aFontEntry->mUserFontData) {
216     aFontEntry->mUserFontData = MakeUnique<gfxUserFontData>();
217   }
218   gfxUserFontData* userFontData = aFontEntry->mUserFontData.get();
219   userFontData->mSrcIndex = aSrcIndex;
220   const gfxFontFaceSrc& src = mSrcList[aSrcIndex];
221   switch (src.mSourceType) {
222     case gfxFontFaceSrc::eSourceType_Local:
223       userFontData->mLocalName = src.mLocalName;
224       break;
225     case gfxFontFaceSrc::eSourceType_URL:
226       userFontData->mURI = src.mURI;
227       userFontData->mPrincipal = mPrincipal;
228       break;
229     case gfxFontFaceSrc::eSourceType_Buffer:
230       userFontData->mIsBuffer = true;
231       break;
232   }
233   userFontData->mPrivate = aPrivate;
234   userFontData->mFormat = src.mFormatFlags;
235   userFontData->mRealName = aOriginalName;
236   if (aMetadata) {
237     userFontData->mMetadata = std::move(*aMetadata);
238     userFontData->mMetaOrigLen = aMetaOrigLen;
239     userFontData->mCompression = aCompression;
240   }
241 }
242 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const243 size_t gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
244   return aMallocSizeOf(this) +
245          mMetadata.ShallowSizeOfExcludingThis(aMallocSizeOf) +
246          mLocalName.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
247          mRealName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
248   // Not counting mURI and mPrincipal, as those will be shared.
249 }
250 
251 /*virtual*/
~gfxUserFontFamily()252 gfxUserFontFamily::~gfxUserFontFamily() {
253   // Should not be dropped by stylo
254   MOZ_ASSERT(NS_IsMainThread());
255 }
256 
LoadPrincipal(const gfxUserFontSet & aFontSet) const257 gfxFontSrcPrincipal* gfxFontFaceSrc::LoadPrincipal(
258     const gfxUserFontSet& aFontSet) const {
259   MOZ_ASSERT(mSourceType == eSourceType_URL);
260   if (mUseOriginPrincipal && mOriginPrincipal) {
261     return mOriginPrincipal;
262   }
263   return aFontSet.GetStandardFontLoadPrincipal();
264 }
265 
GetFamilyNameAndURIForLogging(uint32_t aSrcIndex,nsACString & aFamilyName,nsACString & aURI)266 void gfxUserFontEntry::GetFamilyNameAndURIForLogging(uint32_t aSrcIndex,
267                                                      nsACString& aFamilyName,
268                                                      nsACString& aURI) {
269   aFamilyName = mFamilyName;
270 
271   aURI.Truncate();
272   if (aSrcIndex >= mSrcList.Length()) {
273     aURI.AppendLiteral("(end of source list)");
274   } else {
275     if (mSrcList[aSrcIndex].mURI) {
276       mSrcList[aSrcIndex].mURI->GetSpec(aURI);
277       // If the source URI was very long, elide the middle of it.
278       // In principle, the byte-oriented chopping here could leave us
279       // with partial UTF-8 characters at the point where we cut it,
280       // but it really doesn't matter as this is just for logging.
281       const uint32_t kMaxURILengthForLogging = 256;
282       // UTF-8 ellipsis, with spaces to allow additional wrap opportunities
283       // in the resulting log message
284       const char kEllipsis[] = {' ', '\xE2', '\x80', '\xA6', ' '};
285       if (aURI.Length() > kMaxURILengthForLogging) {
286         aURI.Replace(kMaxURILengthForLogging / 2,
287                      aURI.Length() - kMaxURILengthForLogging, kEllipsis,
288                      ArrayLength(kEllipsis));
289       }
290     } else {
291       aURI.AppendLiteral("(invalid URI)");
292     }
293   }
294 }
295 
296 struct WOFFHeader {
297   AutoSwap_PRUint32 signature;
298   AutoSwap_PRUint32 flavor;
299   AutoSwap_PRUint32 length;
300   AutoSwap_PRUint16 numTables;
301   AutoSwap_PRUint16 reserved;
302   AutoSwap_PRUint32 totalSfntSize;
303   AutoSwap_PRUint16 majorVersion;
304   AutoSwap_PRUint16 minorVersion;
305   AutoSwap_PRUint32 metaOffset;
306   AutoSwap_PRUint32 metaCompLen;
307   AutoSwap_PRUint32 metaOrigLen;
308   AutoSwap_PRUint32 privOffset;
309   AutoSwap_PRUint32 privLen;
310 };
311 
312 struct WOFF2Header {
313   AutoSwap_PRUint32 signature;
314   AutoSwap_PRUint32 flavor;
315   AutoSwap_PRUint32 length;
316   AutoSwap_PRUint16 numTables;
317   AutoSwap_PRUint16 reserved;
318   AutoSwap_PRUint32 totalSfntSize;
319   AutoSwap_PRUint32 totalCompressedSize;
320   AutoSwap_PRUint16 majorVersion;
321   AutoSwap_PRUint16 minorVersion;
322   AutoSwap_PRUint32 metaOffset;
323   AutoSwap_PRUint32 metaCompLen;
324   AutoSwap_PRUint32 metaOrigLen;
325   AutoSwap_PRUint32 privOffset;
326   AutoSwap_PRUint32 privLen;
327 };
328 
329 template <typename HeaderT>
CopyWOFFMetadata(const uint8_t * aFontData,uint32_t aLength,FallibleTArray<uint8_t> * aMetadata,uint32_t * aMetaOrigLen)330 void CopyWOFFMetadata(const uint8_t* aFontData, uint32_t aLength,
331                       FallibleTArray<uint8_t>* aMetadata,
332                       uint32_t* aMetaOrigLen) {
333   // This function may be called with arbitrary, unvalidated "font" data
334   // from @font-face, so it needs to be careful to bounds-check, etc.,
335   // before trying to read anything.
336   // This just saves a copy of the compressed data block; it does NOT check
337   // that the block can be successfully decompressed, or that it contains
338   // well-formed/valid XML metadata.
339   if (aLength < sizeof(HeaderT)) {
340     return;
341   }
342   const HeaderT* woff = reinterpret_cast<const HeaderT*>(aFontData);
343   uint32_t metaOffset = woff->metaOffset;
344   uint32_t metaCompLen = woff->metaCompLen;
345   if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
346     return;
347   }
348   if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
349     return;
350   }
351   if (!aMetadata->SetLength(woff->metaCompLen, fallible)) {
352     return;
353   }
354   memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
355   *aMetaOrigLen = woff->metaOrigLen;
356 }
357 
LoadNextSrc()358 void gfxUserFontEntry::LoadNextSrc() {
359   NS_ASSERTION(mCurrentSrcIndex < mSrcList.Length(),
360                "already at the end of the src list for user font");
361   NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
362                 mUserFontLoadState == STATUS_LOAD_PENDING ||
363                 mUserFontLoadState == STATUS_LOADING) &&
364                    mFontDataLoadingState < LOADING_FAILED,
365                "attempting to load a font that has either completed or failed");
366 
367   if (mUserFontLoadState == STATUS_NOT_LOADED) {
368     SetLoadState(STATUS_LOADING);
369     mFontDataLoadingState = LOADING_STARTED;
370     mUnsupportedFormat = false;
371   } else {
372     // we were already loading; move to the next source,
373     // but don't reset state - if we've already timed out,
374     // that counts against the new download
375     mCurrentSrcIndex++;
376   }
377 
378   DoLoadNextSrc(false);
379 }
380 
ContinueLoad()381 void gfxUserFontEntry::ContinueLoad() {
382   MOZ_ASSERT(mUserFontLoadState == STATUS_LOAD_PENDING);
383   MOZ_ASSERT(mSrcList[mCurrentSrcIndex].mSourceType ==
384              gfxFontFaceSrc::eSourceType_URL);
385 
386   SetLoadState(STATUS_LOADING);
387   DoLoadNextSrc(true);
388   if (LoadState() != STATUS_LOADING) {
389     MOZ_ASSERT(mUserFontLoadState != STATUS_LOAD_PENDING,
390                "Not in parallel traversal, shouldn't get LOAD_PENDING again");
391     // Loading is synchronously finished (loaded from cache or failed). We
392     // need to increment the generation so that we flush the style data to
393     // use the new loaded font face.
394     // Without parallel traversal, we would simply get the right font data
395     // after the first call to DoLoadNextSrc() in this case, so we don't need
396     // to touch the generation to trigger another restyle.
397     // XXX We may want to return synchronously in parallel traversal in those
398     // cases as well if possible, so that we don't have an additional restyle.
399     // That doesn't work currently because Document::GetDocShell (called from
400     // FontFaceSet::CheckFontLoad) dereferences a weak pointer, which is not
401     // allowed in parallel traversal.
402     IncrementGeneration();
403   }
404 }
405 
IgnorePrincipal(gfxFontSrcURI * aURI)406 static bool IgnorePrincipal(gfxFontSrcURI* aURI) {
407   return aURI->InheritsSecurityContext();
408 }
409 
DoLoadNextSrc(bool aForceAsync)410 void gfxUserFontEntry::DoLoadNextSrc(bool aForceAsync) {
411   uint32_t numSrc = mSrcList.Length();
412 
413   // load each src entry in turn, until a local face is found
414   // or a download begins successfully
415   while (mCurrentSrcIndex < numSrc) {
416     gfxFontFaceSrc& currSrc = mSrcList[mCurrentSrcIndex];
417 
418     // src local ==> lookup and load immediately
419 
420     if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
421       // Don't look up local fonts if the font whitelist is being used.
422       gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
423       gfxFontEntry* fe = nullptr;
424       if (!pfl->IsFontFamilyWhitelistActive()) {
425         fe = gfxPlatform::GetPlatform()->LookupLocalFont(
426             currSrc.mLocalName, Weight(), Stretch(), SlantStyle());
427         // Note that we've attempted a local lookup, even if it failed,
428         // as this means we are dependent on any updates to the font list.
429         mSeenLocalSource = true;
430         nsTArray<gfxUserFontSet*> fontSets;
431         GetUserFontSets(fontSets);
432         for (gfxUserFontSet* fontSet : fontSets) {
433           // We need to note on each gfxUserFontSet that contains the user
434           // font entry that we used a local() rule.
435           fontSet->SetLocalRulesUsed();
436         }
437       }
438       if (fe) {
439         LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
440              mFontSet, mCurrentSrcIndex, currSrc.mLocalName.get(),
441              mFamilyName.get(), uint32_t(mFontSet->mGeneration)));
442         fe->mFeatureSettings.AppendElements(mFeatureSettings);
443         fe->mVariationSettings.AppendElements(mVariationSettings);
444         fe->mLanguageOverride = mLanguageOverride;
445         fe->mFamilyName = mFamilyName;
446         fe->mRangeFlags = mRangeFlags;
447         fe->mAscentOverride = mAscentOverride;
448         fe->mDescentOverride = mDescentOverride;
449         fe->mLineGapOverride = mLineGapOverride;
450         fe->mSizeAdjust = mSizeAdjust;
451         // For src:local(), we don't care whether the request is from
452         // a private window as there's no issue of caching resources;
453         // local fonts are just available all the time.
454         StoreUserFontData(fe, mCurrentSrcIndex, false, nsCString(), nullptr, 0,
455                           gfxUserFontData::kUnknownCompression);
456         mPlatformFontEntry = fe;
457         SetLoadState(STATUS_LOADED);
458         Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
459                               currSrc.mSourceType + 1);
460         return;
461       } else {
462         LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", mFontSet,
463              mCurrentSrcIndex, currSrc.mLocalName.get(), mFamilyName.get()));
464       }
465     }
466 
467     // src url ==> start the load process
468     else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
469       if (gfxPlatform::GetPlatform()->IsFontFormatSupported(
470               currSrc.mFormatFlags)) {
471         if (ServoStyleSet* set = ServoStyleSet::Current()) {
472           // Only support style worker threads synchronously getting
473           // entries from the font cache when it's not a data: URI
474           // @font-face that came from UA or user sheets, since we
475           // were not able to call IsFontLoadAllowed ahead of time
476           // for these entries.
477           if (currSrc.mUseOriginPrincipal && IgnorePrincipal(currSrc.mURI)) {
478             set->AppendTask(PostTraversalTask::LoadFontEntry(this));
479             SetLoadState(STATUS_LOAD_PENDING);
480             return;
481           }
482         }
483 
484         // see if we have an existing entry for this source
485         gfxFontEntry* fe =
486             gfxUserFontSet::UserFontCache::GetFont(currSrc, *this);
487         if (fe) {
488           mPlatformFontEntry = fe;
489           SetLoadState(STATUS_LOADED);
490           if (LOG_ENABLED()) {
491             LOG(
492                 ("userfonts (%p) [src %d] "
493                  "loaded uri from cache: (%s) for (%s)\n",
494                  mFontSet, mCurrentSrcIndex,
495                  currSrc.mURI->GetSpecOrDefault().get(), mFamilyName.get()));
496           }
497           return;
498         }
499 
500         if (ServoStyleSet* set = ServoStyleSet::Current()) {
501           // If we need to start a font load and we're on a style
502           // worker thread, we have to defer it.
503           set->AppendTask(PostTraversalTask::LoadFontEntry(this));
504           SetLoadState(STATUS_LOAD_PENDING);
505           return;
506         }
507 
508         // record the principal we should use for the load for use when
509         // creating a channel and when caching the loaded entry.
510         mPrincipal = currSrc.LoadPrincipal(*mFontSet);
511 
512         bool loadDoesntSpin = !aForceAsync && currSrc.mURI->SyncLoadIsOK();
513 
514         if (loadDoesntSpin) {
515           uint8_t* buffer = nullptr;
516           uint32_t bufferLength = 0;
517 
518           // sync load font immediately
519           nsresult rv =
520               mFontSet->SyncLoadFontData(this, &currSrc, buffer, bufferLength);
521 
522           if (NS_SUCCEEDED(rv) &&
523               LoadPlatformFontSync(mCurrentSrcIndex, buffer, bufferLength)) {
524             SetLoadState(STATUS_LOADED);
525             Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
526                                   currSrc.mSourceType + 1);
527             return;
528           } else {
529             mFontSet->LogMessage(this, mCurrentSrcIndex, "font load failed",
530                                  nsIScriptError::errorFlag, rv);
531           }
532 
533         } else {
534           // otherwise load font async
535           nsresult rv = mFontSet->StartLoad(this, mCurrentSrcIndex);
536           bool loadOK = NS_SUCCEEDED(rv);
537 
538           if (loadOK) {
539             if (LOG_ENABLED()) {
540               LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
541                    mFontSet, mCurrentSrcIndex,
542                    currSrc.mURI->GetSpecOrDefault().get(), mFamilyName.get()));
543             }
544             return;
545           } else {
546             mFontSet->LogMessage(this, mCurrentSrcIndex,
547                                  "failed to start download",
548                                  nsIScriptError::errorFlag, rv);
549           }
550         }
551       } else {
552         // We don't log a warning to the web console yet,
553         // as another source may load successfully
554         mUnsupportedFormat = true;
555       }
556     }
557 
558     // FontFace buffer ==> load immediately
559 
560     else {
561       MOZ_ASSERT(currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Buffer);
562 
563       uint8_t* buffer = nullptr;
564       uint32_t bufferLength = 0;
565 
566       // sync load font immediately
567       currSrc.mBuffer->TakeBuffer(buffer, bufferLength);
568       if (buffer &&
569           LoadPlatformFontSync(mCurrentSrcIndex, buffer, bufferLength)) {
570         // LoadPlatformFontSync takes ownership of the buffer, so no need
571         // to free it here.
572         SetLoadState(STATUS_LOADED);
573         Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
574                               currSrc.mSourceType + 1);
575         return;
576       } else {
577         mFontSet->LogMessage(this, mCurrentSrcIndex, "font load failed",
578                              nsIScriptError::errorFlag);
579       }
580     }
581 
582     mCurrentSrcIndex++;
583   }
584 
585   if (mUnsupportedFormat) {
586     mFontSet->LogMessage(this, mCurrentSrcIndex, "no supported format found",
587                          nsIScriptError::warningFlag);
588   }
589 
590   // all src's failed; mark this entry as unusable (so fallback will occur)
591   LOG(("userfonts (%p) failed all src for (%s)\n", mFontSet,
592        mFamilyName.get()));
593   mFontDataLoadingState = LOADING_FAILED;
594   SetLoadState(STATUS_FAILED);
595 }
596 
SetLoadState(UserFontLoadState aLoadState)597 void gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState) {
598   mUserFontLoadState = aLoadState;
599 }
600 
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc)601 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc)
602 
603 bool gfxUserFontEntry::LoadPlatformFontSync(uint32_t aSrcIndex,
604                                             const uint8_t* aFontData,
605                                             uint32_t aLength) {
606   AUTO_PROFILER_LABEL("gfxUserFontEntry::LoadPlatformFontSync", OTHER);
607   NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
608                 mUserFontLoadState == STATUS_LOAD_PENDING ||
609                 mUserFontLoadState == STATUS_LOADING) &&
610                    mFontDataLoadingState < LOADING_FAILED,
611                "attempting to load a font that has either completed or failed");
612 
613   // Unwrap/decompress/sanitize or otherwise munge the downloaded data
614   // to make a usable sfnt structure.
615 
616   // Call the OTS sanitizer; this will also decode WOFF to sfnt
617   // if necessary. The original data in aFontData is left unchanged.
618   uint32_t saneLen;
619   gfxUserFontType fontType;
620   nsTArray<OTSMessage> messages;
621   const uint8_t* saneData =
622       SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType, messages);
623 
624   return LoadPlatformFont(aSrcIndex, aFontData, aLength, fontType, saneData,
625                           saneLen, std::move(messages));
626 }
627 
StartPlatformFontLoadOnBackgroundThread(uint32_t aSrcIndex,const uint8_t * aFontData,uint32_t aLength,nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> aCallback)628 void gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread(
629     uint32_t aSrcIndex, const uint8_t* aFontData, uint32_t aLength,
630     nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> aCallback) {
631   MOZ_ASSERT(!NS_IsMainThread());
632 
633   uint32_t saneLen;
634   gfxUserFontType fontType;
635   nsTArray<OTSMessage> messages;
636   const uint8_t* saneData =
637       SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType, messages);
638 
639   nsCOMPtr<nsIRunnable> event =
640       NewRunnableMethod<uint32_t, const uint8_t*, uint32_t, gfxUserFontType,
641                         const uint8_t*, uint32_t, nsTArray<OTSMessage>&&,
642                         nsMainThreadPtrHandle<nsIFontLoadCompleteCallback>>(
643           "gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread", this,
644           &gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread, aSrcIndex,
645           aFontData, aLength, fontType, saneData, saneLen, std::move(messages),
646           aCallback);
647   NS_DispatchToMainThread(event.forget());
648 }
649 
LoadPlatformFont(uint32_t aSrcIndex,const uint8_t * aOriginalFontData,uint32_t aOriginalLength,gfxUserFontType aFontType,const uint8_t * aSanitizedFontData,uint32_t aSanitizedLength,nsTArray<OTSMessage> && aMessages)650 bool gfxUserFontEntry::LoadPlatformFont(uint32_t aSrcIndex,
651                                         const uint8_t* aOriginalFontData,
652                                         uint32_t aOriginalLength,
653                                         gfxUserFontType aFontType,
654                                         const uint8_t* aSanitizedFontData,
655                                         uint32_t aSanitizedLength,
656                                         nsTArray<OTSMessage>&& aMessages) {
657   MOZ_ASSERT(NS_IsMainThread());
658 
659   for (const auto& msg : aMessages) {
660     mFontSet->LogMessage(this, aSrcIndex, msg.mMessage.get(),
661                          msg.mLevel > 0 ? nsIScriptError::warningFlag
662                                         : nsIScriptError::errorFlag);
663   }
664 
665   if (!aSanitizedFontData) {
666     mFontSet->LogMessage(this, aSrcIndex, "rejected by sanitizer");
667   } else {
668     // Check whether aSanitizedFontData is a known OpenType format; it might be
669     // a TrueType Collection, which OTS would accept but we don't yet
670     // know how to handle. If so, discard.
671     if (gfxFontUtils::DetermineFontDataType(
672             aSanitizedFontData, aSanitizedLength) != GFX_USERFONT_OPENTYPE) {
673       mFontSet->LogMessage(this, aSrcIndex, "not a supported OpenType format");
674       free((void*)aSanitizedFontData);
675       aSanitizedFontData = nullptr;
676     }
677   }
678 
679   // Because platform font activation code may replace the name table
680   // in the font with a synthetic one, we save the original name so that
681   // it can be reported via the InspectorUtils API.
682   nsAutoCString originalFullName;
683 
684   gfxFontEntry* fe = nullptr;
685   uint32_t fontCompressionRatio = 0;
686   size_t computedSize = 0;
687 
688   if (aSanitizedFontData) {
689     if (aSanitizedLength) {
690       fontCompressionRatio =
691           uint32_t(100.0 * aOriginalLength / aSanitizedLength + 0.5);
692       if (aFontType == GFX_USERFONT_WOFF || aFontType == GFX_USERFONT_WOFF2) {
693         Telemetry::Accumulate(aFontType == GFX_USERFONT_WOFF
694                                   ? Telemetry::WEBFONT_COMPRESSION_WOFF
695                                   : Telemetry::WEBFONT_COMPRESSION_WOFF2,
696                               fontCompressionRatio);
697       }
698     }
699 
700     // The sanitizer ensures that we have a valid sfnt and a usable
701     // name table, so this should never fail unless we're out of
702     // memory, and GetFullNameFromSFNT is not directly exposed to
703     // arbitrary/malicious data from the web.
704     gfxFontUtils::GetFullNameFromSFNT(aSanitizedFontData, aSanitizedLength,
705                                       originalFullName);
706 
707     // Record size for memory reporting purposes. We measure this now
708     // because by the time we potentially want to collect reports, this
709     // data block may have been handed off to opaque OS font APIs that
710     // don't allow us to retrieve or measure it directly.
711     // The *OnAlloc function will also tell DMD about this block, as the
712     // OS font code may hold on to it for an extended period.
713     computedSize = UserFontMallocSizeOfOnAlloc(aSanitizedFontData);
714 
715     // Here ownership of aSanitizedFontData is passed to the platform,
716     // which will delete it when no longer required
717     fe = gfxPlatform::GetPlatform()->MakePlatformFont(
718         mName, Weight(), Stretch(), SlantStyle(), aSanitizedFontData,
719         aSanitizedLength);
720     if (!fe) {
721       mFontSet->LogMessage(this, aSrcIndex, "not usable by platform");
722     }
723   }
724 
725   if (fe) {
726     fe->mComputedSizeOfUserFont = computedSize;
727 
728     // Save a copy of the metadata block (if present) for InspectorUtils
729     // to use if required. Ownership of the metadata block will be passed
730     // to the gfxUserFontData record below.
731     FallibleTArray<uint8_t> metadata;
732     uint32_t metaOrigLen = 0;
733     uint8_t compression = gfxUserFontData::kUnknownCompression;
734     if (aFontType == GFX_USERFONT_WOFF) {
735       CopyWOFFMetadata<WOFFHeader>(aOriginalFontData, aOriginalLength,
736                                    &metadata, &metaOrigLen);
737       compression = gfxUserFontData::kZlibCompression;
738     } else if (aFontType == GFX_USERFONT_WOFF2) {
739       CopyWOFFMetadata<WOFF2Header>(aOriginalFontData, aOriginalLength,
740                                     &metadata, &metaOrigLen);
741       compression = gfxUserFontData::kBrotliCompression;
742     }
743 
744     // copy OpenType feature/language settings from the userfont entry to the
745     // newly-created font entry
746     fe->mFeatureSettings.AppendElements(mFeatureSettings);
747     fe->mVariationSettings.AppendElements(mVariationSettings);
748     fe->mLanguageOverride = mLanguageOverride;
749     fe->mFamilyName = mFamilyName;
750     fe->mRangeFlags = mRangeFlags;
751     fe->mAscentOverride = mAscentOverride;
752     fe->mDescentOverride = mDescentOverride;
753     fe->mLineGapOverride = mLineGapOverride;
754     fe->mSizeAdjust = mSizeAdjust;
755     StoreUserFontData(fe, aSrcIndex, mFontSet->GetPrivateBrowsing(),
756                       originalFullName, &metadata, metaOrigLen, compression);
757     if (LOG_ENABLED()) {
758       LOG((
759           "userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
760           "(%p) gen: %8.8x compress: %d%%\n",
761           mFontSet, aSrcIndex,
762           mSrcList[aSrcIndex].mURI->GetSpecOrDefault().get(), mFamilyName.get(),
763           this, uint32_t(mFontSet->mGeneration), fontCompressionRatio));
764     }
765     mPlatformFontEntry = fe;
766     SetLoadState(STATUS_LOADED);
767     gfxUserFontSet::UserFontCache::CacheFont(fe);
768   } else {
769     if (LOG_ENABLED()) {
770       LOG(
771           ("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
772            " error making platform font\n",
773            mFontSet, aSrcIndex,
774            mSrcList[aSrcIndex].mURI->GetSpecOrDefault().get(),
775            mFamilyName.get()));
776     }
777   }
778 
779   // The downloaded data can now be discarded; the font entry is using the
780   // sanitized copy
781   free((void*)aOriginalFontData);
782 
783   return fe != nullptr;
784 }
785 
Load()786 void gfxUserFontEntry::Load() {
787   if (mUserFontLoadState == STATUS_NOT_LOADED) {
788     LoadNextSrc();
789   }
790 }
791 
IncrementGeneration()792 void gfxUserFontEntry::IncrementGeneration() {
793   nsTArray<gfxUserFontSet*> fontSets;
794   GetUserFontSets(fontSets);
795   for (gfxUserFontSet* fontSet : fontSets) {
796     fontSet->IncrementGeneration();
797   }
798 }
799 
800 // This is called when a font download finishes.
801 // Ownership of aFontData passes in here, and the font set must
802 // ensure that it is eventually deleted via free().
FontDataDownloadComplete(uint32_t aSrcIndex,const uint8_t * aFontData,uint32_t aLength,nsresult aDownloadStatus,nsIFontLoadCompleteCallback * aCallback)803 void gfxUserFontEntry::FontDataDownloadComplete(
804     uint32_t aSrcIndex, const uint8_t* aFontData, uint32_t aLength,
805     nsresult aDownloadStatus, nsIFontLoadCompleteCallback* aCallback) {
806   MOZ_ASSERT(NS_IsMainThread());
807 
808   // forget about the loader, as we no longer potentially need to cancel it
809   // if the entry is obsoleted
810   mLoader = nullptr;
811 
812   // download successful, make platform font using font data
813   if (NS_SUCCEEDED(aDownloadStatus) &&
814       mFontDataLoadingState != LOADING_TIMED_OUT) {
815     if (StaticPrefs::gfx_downloadable_fonts_sanitize_omt()) {
816       LoadPlatformFontAsync(aSrcIndex, aFontData, aLength, aCallback);
817     } else {
818       bool loaded = LoadPlatformFontSync(aSrcIndex, aFontData, aLength);
819       aFontData = nullptr;
820       if (loaded) {
821         IncrementGeneration();
822         aCallback->FontLoadComplete();
823       } else {
824         FontLoadFailed(aCallback);
825       }
826     }
827     return;
828   }
829 
830   // download failed or font-display timeout passed
831   if (mFontDataLoadingState == LOADING_TIMED_OUT) {
832     mFontSet->LogMessage(this, aSrcIndex,
833                          "font-display timeout, webfont not used",
834                          nsIScriptError::infoFlag, aDownloadStatus);
835   } else {
836     mFontSet->LogMessage(this, aSrcIndex, "download failed",
837                          nsIScriptError::errorFlag, aDownloadStatus);
838   }
839 
840   if (aFontData) {
841     free((void*)aFontData);
842   }
843 
844   FontLoadFailed(aCallback);
845 }
846 
LoadPlatformFontAsync(uint32_t aSrcIndex,const uint8_t * aFontData,uint32_t aLength,nsIFontLoadCompleteCallback * aCallback)847 void gfxUserFontEntry::LoadPlatformFontAsync(
848     uint32_t aSrcIndex, const uint8_t* aFontData, uint32_t aLength,
849     nsIFontLoadCompleteCallback* aCallback) {
850   nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> cb(
851       new nsMainThreadPtrHolder<nsIFontLoadCompleteCallback>("FontLoader",
852                                                              aCallback));
853 
854   // Do the OpenType sanitization over on the font loading thread.  Once that is
855   // complete, we'll continue in ContinuePlatformFontLoadOnMainThread.
856   //
857   // We hold a strong reference to the gfxUserFontSet during this work, since
858   // the document might be closed while we are OMT, and release it at the end
859   // of ContinuePlatformFontLoadOnMainThread.
860 
861   mFontSet->AddRef();
862 
863   nsCOMPtr<nsIRunnable> event =
864       NewRunnableMethod<uint32_t, const uint8_t*, uint32_t,
865                         nsMainThreadPtrHandle<nsIFontLoadCompleteCallback>>(
866           "gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread", this,
867           &gfxUserFontEntry::StartPlatformFontLoadOnBackgroundThread, aSrcIndex,
868           aFontData, aLength, cb);
869   MOZ_ALWAYS_SUCCEEDS(NS_DispatchBackgroundTask(event.forget()));
870 }
871 
ContinuePlatformFontLoadOnMainThread(uint32_t aSrcIndex,const uint8_t * aOriginalFontData,uint32_t aOriginalLength,gfxUserFontType aFontType,const uint8_t * aSanitizedFontData,uint32_t aSanitizedLength,nsTArray<OTSMessage> && aMessages,nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> aCallback)872 void gfxUserFontEntry::ContinuePlatformFontLoadOnMainThread(
873     uint32_t aSrcIndex, const uint8_t* aOriginalFontData,
874     uint32_t aOriginalLength, gfxUserFontType aFontType,
875     const uint8_t* aSanitizedFontData, uint32_t aSanitizedLength,
876     nsTArray<OTSMessage>&& aMessages,
877     nsMainThreadPtrHandle<nsIFontLoadCompleteCallback> aCallback) {
878   MOZ_ASSERT(NS_IsMainThread());
879 
880   bool loaded = LoadPlatformFont(aSrcIndex, aOriginalFontData, aOriginalLength,
881                                  aFontType, aSanitizedFontData,
882                                  aSanitizedLength, std::move(aMessages));
883   aOriginalFontData = nullptr;
884   aSanitizedFontData = nullptr;
885 
886   if (loaded) {
887     IncrementGeneration();
888     aCallback->FontLoadComplete();
889   } else {
890     FontLoadFailed(aCallback);
891   }
892 
893   mFontSet->Release();  // for the AddRef in LoadPlatformFontAsync
894 }
895 
FontLoadFailed(nsIFontLoadCompleteCallback * aCallback)896 void gfxUserFontEntry::FontLoadFailed(nsIFontLoadCompleteCallback* aCallback) {
897   MOZ_ASSERT(NS_IsMainThread());
898 
899   // Error occurred.  Make sure the FontFace's promise is rejected if the
900   // load timed out, or else load the next src.
901   if (mFontDataLoadingState == LOADING_TIMED_OUT) {
902     mFontDataLoadingState = LOADING_FAILED;
903     SetLoadState(STATUS_FAILED);
904   } else {
905     LoadNextSrc();
906   }
907 
908   // We ignore the status returned by LoadNext();
909   // even if loading failed, we need to bump the font-set generation
910   // and return true in order to trigger reflow, so that fallback
911   // will be used where the text was "masked" by the pending download
912   IncrementGeneration();
913   aCallback->FontLoadComplete();
914 }
915 
GetUserFontSets(nsTArray<gfxUserFontSet * > & aResult)916 void gfxUserFontEntry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult) {
917   aResult.Clear();
918   aResult.AppendElement(mFontSet);
919 }
920 
gfxUserFontSet()921 gfxUserFontSet::gfxUserFontSet()
922     : mFontFamilies(4),
923       mRebuildGeneration(0),
924       mLocalRulesUsed(false),
925       mRebuildLocalRules(false),
926       mDownloadCount(0),
927       mDownloadSize(0) {
928   IncrementGeneration(true);
929   gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
930   if (fp) {
931     fp->AddUserFontSet(this);
932   }
933 }
934 
~gfxUserFontSet()935 gfxUserFontSet::~gfxUserFontSet() {
936   gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
937   if (fp) {
938     fp->RemoveUserFontSet(this);
939   }
940 }
941 
FindOrCreateUserFontEntry(const nsACString & aFamilyName,const nsTArray<gfxFontFaceSrc> & aFontFaceSrcList,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,const nsTArray<gfxFontFeature> & aFeatureSettings,const nsTArray<gfxFontVariation> & aVariationSettings,uint32_t aLanguageOverride,gfxCharacterMap * aUnicodeRanges,StyleFontDisplay aFontDisplay,RangeFlags aRangeFlags,float aAscentOverride,float aDescentOverride,float aLineGapOverride,float aSizeAdjust)942 already_AddRefed<gfxUserFontEntry> gfxUserFontSet::FindOrCreateUserFontEntry(
943     const nsACString& aFamilyName,
944     const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, WeightRange aWeight,
945     StretchRange aStretch, SlantStyleRange aStyle,
946     const nsTArray<gfxFontFeature>& aFeatureSettings,
947     const nsTArray<gfxFontVariation>& aVariationSettings,
948     uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
949     StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags,
950     float aAscentOverride, float aDescentOverride, float aLineGapOverride,
951     float aSizeAdjust) {
952   RefPtr<gfxUserFontEntry> entry;
953 
954   // If there's already a userfont entry in the family whose descriptors all
955   // match, we can just move it to the end of the list instead of adding a new
956   // face that will always "shadow" the old one.
957   // Note that we can't do this for platform font entries, even if the
958   // style descriptors match, as they might have had a different source list,
959   // but we no longer have the old source list available to check.
960   gfxUserFontFamily* family = LookupFamily(aFamilyName);
961   if (family) {
962     entry = FindExistingUserFontEntry(
963         family, aFontFaceSrcList, aWeight, aStretch, aStyle, aFeatureSettings,
964         aVariationSettings, aLanguageOverride, aUnicodeRanges, aFontDisplay,
965         aRangeFlags, aAscentOverride, aDescentOverride, aLineGapOverride,
966         aSizeAdjust);
967   }
968 
969   if (!entry) {
970     entry = CreateUserFontEntry(aFontFaceSrcList, aWeight, aStretch, aStyle,
971                                 aFeatureSettings, aVariationSettings,
972                                 aLanguageOverride, aUnicodeRanges, aFontDisplay,
973                                 aRangeFlags, aAscentOverride, aDescentOverride,
974                                 aLineGapOverride, aSizeAdjust);
975     entry->mFamilyName = aFamilyName;
976   }
977 
978   return entry.forget();
979 }
980 
FindExistingUserFontEntry(gfxUserFontFamily * aFamily,const nsTArray<gfxFontFaceSrc> & aFontFaceSrcList,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,const nsTArray<gfxFontFeature> & aFeatureSettings,const nsTArray<gfxFontVariation> & aVariationSettings,uint32_t aLanguageOverride,gfxCharacterMap * aUnicodeRanges,StyleFontDisplay aFontDisplay,RangeFlags aRangeFlags,float aAscentOverride,float aDescentOverride,float aLineGapOverride,float aSizeAdjust)981 gfxUserFontEntry* gfxUserFontSet::FindExistingUserFontEntry(
982     gfxUserFontFamily* aFamily,
983     const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, WeightRange aWeight,
984     StretchRange aStretch, SlantStyleRange aStyle,
985     const nsTArray<gfxFontFeature>& aFeatureSettings,
986     const nsTArray<gfxFontVariation>& aVariationSettings,
987     uint32_t aLanguageOverride, gfxCharacterMap* aUnicodeRanges,
988     StyleFontDisplay aFontDisplay, RangeFlags aRangeFlags,
989     float aAscentOverride, float aDescentOverride, float aLineGapOverride,
990     float aSizeAdjust) {
991   nsTArray<RefPtr<gfxFontEntry>>& fontList = aFamily->GetFontList();
992 
993   for (size_t i = 0, count = fontList.Length(); i < count; i++) {
994     if (!fontList[i]->mIsUserFontContainer) {
995       continue;
996     }
997 
998     gfxUserFontEntry* existingUserFontEntry =
999         static_cast<gfxUserFontEntry*>(fontList[i].get());
1000     if (!existingUserFontEntry->Matches(
1001             aFontFaceSrcList, aWeight, aStretch, aStyle, aFeatureSettings,
1002             aVariationSettings, aLanguageOverride, aUnicodeRanges, aFontDisplay,
1003             aRangeFlags, aAscentOverride, aDescentOverride, aLineGapOverride,
1004             aSizeAdjust)) {
1005       continue;
1006     }
1007 
1008     return existingUserFontEntry;
1009   }
1010 
1011   return nullptr;
1012 }
1013 
AddUserFontEntry(const nsCString & aFamilyName,gfxUserFontEntry * aUserFontEntry)1014 void gfxUserFontSet::AddUserFontEntry(const nsCString& aFamilyName,
1015                                       gfxUserFontEntry* aUserFontEntry) {
1016   gfxUserFontFamily* family = GetFamily(aFamilyName);
1017   family->AddFontEntry(aUserFontEntry);
1018 
1019   if (LOG_ENABLED()) {
1020     nsAutoCString weightString;
1021     aUserFontEntry->Weight().ToString(weightString);
1022     nsAutoCString stretchString;
1023     aUserFontEntry->Stretch().ToString(stretchString);
1024     LOG(
1025         ("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
1026          "stretch: %s display: %d",
1027          this, aFamilyName.get(), aUserFontEntry,
1028          (aUserFontEntry->IsItalic()
1029               ? "italic"
1030               : (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
1031          weightString.get(), stretchString.get(),
1032          static_cast<int>(aUserFontEntry->GetFontDisplay())));
1033   }
1034 }
1035 
IncrementGeneration(bool aIsRebuild)1036 void gfxUserFontSet::IncrementGeneration(bool aIsRebuild) {
1037   // add one, increment again if zero
1038   ++sFontSetGeneration;
1039   if (sFontSetGeneration == 0) ++sFontSetGeneration;
1040   mGeneration = sFontSetGeneration;
1041   if (aIsRebuild) {
1042     mRebuildGeneration = mGeneration;
1043   }
1044 }
1045 
RebuildLocalRules()1046 void gfxUserFontSet::RebuildLocalRules() {
1047   if (mLocalRulesUsed) {
1048     mRebuildLocalRules = true;
1049     DoRebuildUserFontSet();
1050   }
1051 }
1052 
LookupFamily(const nsACString & aFamilyName) const1053 gfxUserFontFamily* gfxUserFontSet::LookupFamily(
1054     const nsACString& aFamilyName) const {
1055   nsAutoCString key(aFamilyName);
1056   ToLowerCase(key);
1057 
1058   return mFontFamilies.GetWeak(key);
1059 }
1060 
GetFamily(const nsACString & aFamilyName)1061 gfxUserFontFamily* gfxUserFontSet::GetFamily(const nsACString& aFamilyName) {
1062   nsAutoCString key(aFamilyName);
1063   ToLowerCase(key);
1064 
1065   return mFontFamilies.GetOrInsertNew(key, aFamilyName);
1066 }
1067 
ForgetLocalFaces()1068 void gfxUserFontSet::ForgetLocalFaces() {
1069   for (const auto& fam : mFontFamilies.Values()) {
1070     const auto& fonts = fam->GetFontList();
1071     for (const auto& f : fonts) {
1072       auto ufe = static_cast<gfxUserFontEntry*>(f.get());
1073       // If the user font entry has loaded an entry using src:local(),
1074       // discard it as no longer valid.
1075       if (ufe->GetPlatformFontEntry() &&
1076           ufe->GetPlatformFontEntry()->IsLocalUserFont()) {
1077         ufe->mPlatformFontEntry = nullptr;
1078       }
1079       // We need to re-evaluate the source list in the context of the new
1080       // platform fontlist, whether or not the entry actually used a local()
1081       // source last time, as one might be newly available.
1082       if (ufe->mSeenLocalSource) {
1083         ufe->LoadCanceled();
1084       }
1085     }
1086   }
1087 }
1088 
1089 ///////////////////////////////////////////////////////////////////////////////
1090 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
1091 // across pages/fontsets rather than instantiating new platform fonts.
1092 //
1093 // Entries are added to this cache when a platform font is instantiated from
1094 // downloaded data, and removed when the platform font entry is destroyed.
1095 // We don't need to use a timed expiration scheme here because the gfxFontEntry
1096 // for a downloaded font will be kept alive by its corresponding gfxFont
1097 // instance(s) until they are deleted, and *that* happens using an expiration
1098 // tracker (gfxFontCache). The result is that the downloaded font instances
1099 // recorded here will persist between pages and can get reused (provided the
1100 // source URI and principal match, of course).
1101 ///////////////////////////////////////////////////////////////////////////////
1102 
1103 nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
1104     gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
1105 
NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher,nsIObserver)1106 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
1107 
1108 NS_IMETHODIMP
1109 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
1110                                                 const char* aTopic,
1111                                                 const char16_t* aData) {
1112   if (!sUserFonts) {
1113     return NS_OK;
1114   }
1115 
1116   if (!strcmp(aTopic, "cacheservice:empty-cache")) {
1117     for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1118       i.Remove();
1119     }
1120   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
1121     for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1122       if (i.Get()->IsPrivate()) {
1123         i.Remove();
1124       }
1125     }
1126   } else if (!strcmp(aTopic, "xpcom-shutdown")) {
1127     for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1128       i.Get()->GetFontEntry()->DisconnectSVG();
1129     }
1130   } else {
1131     MOZ_ASSERT_UNREACHABLE("unexpected topic");
1132   }
1133 
1134   return NS_OK;
1135 }
1136 
KeyEquals(const KeyTypePointer aKey) const1137 bool gfxUserFontSet::UserFontCache::Entry::KeyEquals(
1138     const KeyTypePointer aKey) const {
1139   const gfxFontEntry* fe = aKey->mFontEntry;
1140 
1141   if (!mURI->Equals(aKey->mURI)) {
1142     return false;
1143   }
1144 
1145   // For data: URIs, we don't care about the principal; otherwise, check it.
1146   if (!IgnorePrincipal(mURI)) {
1147     NS_ASSERTION(mPrincipal && aKey->mPrincipal,
1148                  "only data: URIs are allowed to omit the principal");
1149     if (!mPrincipal->Equals(aKey->mPrincipal)) {
1150       return false;
1151     }
1152   }
1153 
1154   if (mPrivate != aKey->mPrivate) {
1155     return false;
1156   }
1157 
1158   if (mFontEntry->SlantStyle() != fe->SlantStyle() ||
1159       mFontEntry->Weight() != fe->Weight() ||
1160       mFontEntry->Stretch() != fe->Stretch() ||
1161       mFontEntry->mFeatureSettings != fe->mFeatureSettings ||
1162       mFontEntry->mVariationSettings != fe->mVariationSettings ||
1163       mFontEntry->mLanguageOverride != fe->mLanguageOverride ||
1164       mFontEntry->mAscentOverride != fe->mAscentOverride ||
1165       mFontEntry->mDescentOverride != fe->mDescentOverride ||
1166       mFontEntry->mLineGapOverride != fe->mLineGapOverride ||
1167       mFontEntry->mSizeAdjust != fe->mSizeAdjust ||
1168       mFontEntry->mFamilyName != fe->mFamilyName) {
1169     return false;
1170   }
1171 
1172   return true;
1173 }
1174 
CacheFont(gfxFontEntry * aFontEntry)1175 void gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry) {
1176   NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
1177                "caching a font associated with no family yet");
1178 
1179   // if caching is disabled, simply return
1180   if (Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1181     return;
1182   }
1183 
1184   gfxUserFontData* data = aFontEntry->mUserFontData.get();
1185   if (data->mIsBuffer) {
1186 #ifdef DEBUG_USERFONT_CACHE
1187     printf("userfontcache skipped fontentry with buffer source: %p\n",
1188            aFontEntry);
1189 #endif
1190     return;
1191   }
1192 
1193   if (!sUserFonts) {
1194     sUserFonts = new nsTHashtable<Entry>;
1195 
1196     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1197     if (obs) {
1198       Flusher* flusher = new Flusher;
1199       obs->AddObserver(flusher, "cacheservice:empty-cache", false);
1200       obs->AddObserver(flusher, "last-pb-context-exited", false);
1201       obs->AddObserver(flusher, "xpcom-shutdown", false);
1202     }
1203 
1204     // Create and register a memory reporter for sUserFonts.
1205     // This reporter is never unregistered, but that's OK because
1206     // the reporter checks whether sUserFonts is null, so it would
1207     // be safe to call even after UserFontCache::Shutdown has deleted
1208     // the cache.
1209     RegisterStrongMemoryReporter(new MemoryReporter());
1210   }
1211 
1212   // For data: URIs, the principal is ignored; anyone who has the same
1213   // data: URI is able to load it and get an equivalent font.
1214   // Otherwise, the principal is used as part of the cache key.
1215   gfxFontSrcPrincipal* principal;
1216   if (IgnorePrincipal(data->mURI)) {
1217     principal = nullptr;
1218   } else {
1219     principal = data->mPrincipal;
1220   }
1221   sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry, data->mPrivate));
1222 
1223 #ifdef DEBUG_USERFONT_CACHE
1224   printf("userfontcache added fontentry: %p\n", aFontEntry);
1225   Dump();
1226 #endif
1227 }
1228 
ForgetFont(gfxFontEntry * aFontEntry)1229 void gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry) {
1230   if (!sUserFonts) {
1231     // if we've already deleted the cache (i.e. during shutdown),
1232     // just ignore this
1233     return;
1234   }
1235 
1236   // We can't simply use RemoveEntry here because it's possible the principal
1237   // may have changed since the font was cached, in which case the lookup
1238   // would no longer find the entry (bug 838105).
1239   for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1240     if (i.Get()->GetFontEntry() == aFontEntry) {
1241       i.Remove();
1242     }
1243   }
1244 
1245 #ifdef DEBUG_USERFONT_CACHE
1246   printf("userfontcache removed fontentry: %p\n", aFontEntry);
1247   Dump();
1248 #endif
1249 }
1250 
GetFont(const gfxFontFaceSrc & aSrc,const gfxUserFontEntry & aUserFontEntry)1251 gfxFontEntry* gfxUserFontSet::UserFontCache::GetFont(
1252     const gfxFontFaceSrc& aSrc, const gfxUserFontEntry& aUserFontEntry) {
1253   if (!sUserFonts || aUserFontEntry.mFontSet->BypassCache() ||
1254       Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1255     return nullptr;
1256   }
1257 
1258   // Ignore principal when looking up a data: URI.
1259   gfxFontSrcPrincipal* principal =
1260       IgnorePrincipal(aSrc.mURI) ? nullptr
1261                                  : aSrc.LoadPrincipal(*aUserFontEntry.mFontSet);
1262 
1263   Entry* entry = sUserFonts->GetEntry(
1264       Key(aSrc.mURI, principal, const_cast<gfxUserFontEntry*>(&aUserFontEntry),
1265           aUserFontEntry.mFontSet->GetPrivateBrowsing()));
1266   if (!entry) {
1267     return nullptr;
1268   }
1269 
1270   // We have to perform another content policy check here to prevent
1271   // cache poisoning. E.g. a.com loads a font into the cache but
1272   // b.com has a CSP not allowing any fonts to be loaded.
1273   if (!aUserFontEntry.mFontSet->IsFontLoadAllowed(aSrc)) {
1274     return nullptr;
1275   }
1276 
1277   return entry->GetFontEntry();
1278 }
1279 
Shutdown()1280 void gfxUserFontSet::UserFontCache::Shutdown() {
1281   if (sUserFonts) {
1282     delete sUserFonts;
1283     sUserFonts = nullptr;
1284   }
1285 }
1286 
MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)1287 MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
1288 
1289 void gfxUserFontSet::UserFontCache::Entry::ReportMemory(
1290     nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1291     bool aAnonymize) {
1292   MOZ_ASSERT(mFontEntry);
1293   nsAutoCString path("explicit/gfx/user-fonts/font(");
1294 
1295   if (aAnonymize) {
1296     path.AppendPrintf("<anonymized-%p>", this);
1297   } else {
1298     path.AppendPrintf("family=%s", mFontEntry->mFamilyName.get());
1299     if (mURI) {
1300       nsCString spec = mURI->GetSpecOrDefault();
1301       spec.ReplaceChar('/', '\\');
1302       // Some fonts are loaded using horrendously-long data: URIs;
1303       // truncate those before reporting them.
1304       if (mURI->get()->SchemeIs("data") && spec.Length() > 255) {
1305         spec.Truncate(252);
1306         spec.AppendLiteral("...");
1307       }
1308       path.AppendPrintf(", url=%s", spec.get());
1309     }
1310     if (mPrincipal) {
1311       nsAutoCString spec;
1312       mPrincipal->NodePrincipal()->GetAsciiSpec(spec);
1313       if (!spec.IsEmpty()) {
1314         // Include a clue as to who loaded this resource. (Note
1315         // that because of font entry sharing, other pages may now
1316         // be using this resource, and the original page may not
1317         // even be loaded any longer.)
1318         spec.ReplaceChar('/', '\\');
1319         path.AppendPrintf(", principal=%s", spec.get());
1320       }
1321     }
1322   }
1323   path.Append(')');
1324 
1325   aHandleReport->Callback(
1326       ""_ns, path, nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
1327       mFontEntry->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf),
1328       "Memory used by @font-face resource."_ns, aData);
1329 }
1330 
NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter,nsIMemoryReporter)1331 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter,
1332                   nsIMemoryReporter)
1333 
1334 NS_IMETHODIMP
1335 gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
1336     nsIHandleReportCallback* aHandleReport, nsISupports* aData,
1337     bool aAnonymize) {
1338   if (!sUserFonts) {
1339     return NS_OK;
1340   }
1341 
1342   for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1343     it.Get()->ReportMemory(aHandleReport, aData, aAnonymize);
1344   }
1345 
1346   MOZ_COLLECT_REPORT(
1347       "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP, UNITS_BYTES,
1348       sUserFonts->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf),
1349       "Memory used by the @font-face cache, not counting the actual font "
1350       "resources.");
1351 
1352   return NS_OK;
1353 }
1354 
1355 #ifdef DEBUG_USERFONT_CACHE
1356 
Dump()1357 void gfxUserFontSet::UserFontCache::Entry::Dump() {
1358   nsresult rv;
1359 
1360   nsAutoCString principalURISpec("(null)");
1361   bool setDomain = false;
1362 
1363   if (mPrincipal) {
1364     nsCOMPtr<nsIURI> principalURI;
1365     rv = mPrincipal->NodePrincipal()->GetURI(getter_AddRefs(principalURI));
1366     if (NS_SUCCEEDED(rv)) {
1367       principalURI->GetSpec(principalURISpec);
1368     }
1369 
1370     nsCOMPtr<nsIURI> domainURI;
1371     mPrincipal->NodePrincipal()->GetDomain(getter_AddRefs(domainURI));
1372     if (domainURI) {
1373       setDomain = true;
1374     }
1375   }
1376 
1377   NS_ASSERTION(mURI, "null URI in userfont cache entry");
1378 
1379   printf(
1380       "userfontcache fontEntry: %p fonturihash: %8.8x "
1381       "family: %s domainset: %s principal: [%s]\n",
1382       mFontEntry, mURI->Hash(), mFontEntry->FamilyName().get(),
1383       setDomain ? "true" : "false", principalURISpec.get());
1384 }
1385 
Dump()1386 void gfxUserFontSet::UserFontCache::Dump() {
1387   if (!sUserFonts) {
1388     return;
1389   }
1390 
1391   printf("userfontcache dump count: %d ========\n", sUserFonts->Count());
1392   for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1393     it.Get()->Dump();
1394   }
1395   printf("userfontcache dump ==================\n");
1396 }
1397 
1398 #endif
1399 
1400 #undef LOG
1401 #undef LOG_ENABLED
1402