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