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