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