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/. */
7 #define FORCE_PR_LOG /* Allow logging in the release build */
8 #endif /* MOZ_LOGGING */
11 #include "gfxUserFontSet.h"
12 #include "gfxPlatform.h"
13 #include "nsUnicharUtils.h"
14 #include "nsNetUtil.h"
15 #include "nsIJARChannel.h"
16 #include "nsIProtocolHandler.h"
17 #include "nsIPrincipal.h"
18 #include "nsIZipReader.h"
19 #include "gfxFontConstants.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/gfx/2D.h"
22 #include "gfxPlatformFontList.h"
24 #include "opentype-sanitiser.h"
25 #include "ots-memory-stream.h"
27 using namespace mozilla
;
31 gfxUserFontSet::GetUserFontsLog()
33 static PRLogModuleInfo
*sLog
;
35 sLog
= PR_NewLogModule("userfonts");
38 #endif /* PR_LOGGING */
40 #define LOG(args) PR_LOG(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG, args)
41 #define LOG_ENABLED() PR_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), PR_LOG_DEBUG)
43 static uint64_t sFontSetGeneration
= 0;
45 // Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
46 // adapted to use Mozilla allocators and to allow the final
47 // memory buffer to be adopted by the client.
48 class ExpandingMemoryStream
: public ots::OTSStream
{
50 ExpandingMemoryStream(size_t initial
, size_t limit
)
51 : mLength(initial
), mLimit(limit
), mOff(0) {
52 mPtr
= NS_Alloc(mLength
);
55 ~ExpandingMemoryStream() {
59 // return the buffer, and give up ownership of it
60 // so the caller becomes responsible to call NS_Free
61 // when finished with it
68 bool WriteRaw(const void *data
, size_t length
) {
69 if ((mOff
+ length
> mLength
) ||
70 (mLength
> std::numeric_limits
<size_t>::max() - mOff
)) {
71 if (mLength
== mLimit
) {
74 size_t newLength
= (mLength
+ 1) * 2;
75 if (newLength
< mLength
) {
78 if (newLength
> mLimit
) {
81 mPtr
= NS_Realloc(mPtr
, newLength
);
83 return WriteRaw(data
, length
);
85 std::memcpy(static_cast<char*>(mPtr
) + mOff
, data
, length
);
90 bool Seek(off_t position
) {
94 if (static_cast<size_t>(position
) > mLength
) {
112 // TODO: support for unicode ranges not yet implemented
114 gfxProxyFontEntry::gfxProxyFontEntry(gfxUserFontSet
*aFontSet
,
115 const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
118 uint32_t aItalicStyle
,
119 const nsTArray
<gfxFontFeature
>& aFeatureSettings
,
120 uint32_t aLanguageOverride
,
121 gfxSparseBitSet
*aUnicodeRanges
)
122 : gfxFontEntry(NS_LITERAL_STRING("Proxy")),
123 mLoadingState(NOT_LOADING
),
124 mUnsupportedFormat(false),
129 mSrcList
= aFontFaceSrcList
;
133 // XXX Currently, we don't distinguish 'italic' and 'oblique' styles;
134 // we need to fix this. (Bug 543715)
135 mItalic
= (aItalicStyle
& (NS_FONT_STYLE_ITALIC
| NS_FONT_STYLE_OBLIQUE
)) != 0;
136 mFeatureSettings
.AppendElements(aFeatureSettings
);
137 mLanguageOverride
= aLanguageOverride
;
141 gfxProxyFontEntry::~gfxProxyFontEntry()
146 gfxProxyFontEntry::Matches(const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
149 uint32_t aItalicStyle
,
150 const nsTArray
<gfxFontFeature
>& aFeatureSettings
,
151 uint32_t aLanguageOverride
,
152 gfxSparseBitSet
*aUnicodeRanges
)
154 // XXX font entries don't distinguish italic from oblique (bug 543715)
156 (aItalicStyle
& (NS_FONT_STYLE_ITALIC
| NS_FONT_STYLE_OBLIQUE
)) != 0;
158 return mWeight
== aWeight
&&
159 mStretch
== aStretch
&&
160 mItalic
== isItalic
&&
161 mFeatureSettings
== aFeatureSettings
&&
162 mLanguageOverride
== aLanguageOverride
&&
163 mSrcList
== aFontFaceSrcList
;
164 // XXX once we support unicode-range (bug 475891),
165 // we'll need to compare that here as well
169 gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle
*aFontStyle
, bool aNeedsBold
)
171 // cannot create an actual font for a proxy entry
175 class gfxOTSContext
: public ots::OTSContext
{
177 gfxOTSContext(gfxMixedFontFamily
*aFamily
, gfxProxyFontEntry
*aProxy
)
178 : mFamily(aFamily
), mProxy(aProxy
) {}
180 virtual ots::TableAction
GetTableAction(uint32_t aTag
) MOZ_OVERRIDE
{
181 // preserve Graphite, color glyph and SVG tables
182 if (aTag
== TRUETYPE_TAG('S', 'i', 'l', 'f') ||
183 aTag
== TRUETYPE_TAG('S', 'i', 'l', 'l') ||
184 aTag
== TRUETYPE_TAG('G', 'l', 'o', 'c') ||
185 aTag
== TRUETYPE_TAG('G', 'l', 'a', 't') ||
186 aTag
== TRUETYPE_TAG('F', 'e', 'a', 't') ||
187 aTag
== TRUETYPE_TAG('S', 'V', 'G', ' ') ||
188 aTag
== TRUETYPE_TAG('C', 'O', 'L', 'R') ||
189 aTag
== TRUETYPE_TAG('C', 'P', 'A', 'L')) {
190 return ots::TABLE_ACTION_PASSTHRU
;
192 return ots::TABLE_ACTION_DEFAULT
;
195 virtual void Message(int level
, const char* format
,
196 ...) MSGFUNC_FMT_ATTR MOZ_OVERRIDE
{
198 va_start(va
, format
);
201 msg
.AppendPrintf(format
, va
);
206 // For warnings (rather than errors that cause the font to fail),
207 // we only report the first instance of any given message.
208 if (mWarningsIssued
.Contains(msg
)) {
211 mWarningsIssued
.PutEntry(msg
);
214 mProxy
->mFontSet
->LogMessage(mFamily
, mProxy
, msg
.get());
218 gfxMixedFontFamily
*mFamily
;
219 gfxProxyFontEntry
*mProxy
;
220 nsTHashtable
<nsCStringHashKey
> mWarningsIssued
;
223 // Call the OTS library to sanitize an sfnt before attempting to use it.
224 // Returns a newly-allocated block, or nullptr in case of fatal errors.
226 gfxProxyFontEntry::SanitizeOpenTypeData(gfxMixedFontFamily
*aFamily
,
227 const uint8_t* aData
,
229 uint32_t& aSaneLength
,
232 // limit output/expansion to 256MB
233 ExpandingMemoryStream
output(aIsCompressed
? aLength
* 2 : aLength
,
236 gfxOTSContext
otsContext(aFamily
, this);
238 if (otsContext
.Process(&output
, aData
, aLength
)) {
239 aSaneLength
= output
.Tell();
240 return static_cast<uint8_t*>(output
.forget());
248 gfxProxyFontEntry::StoreUserFontData(gfxFontEntry
* aFontEntry
,
250 const nsAString
& aOriginalName
,
251 FallibleTArray
<uint8_t>* aMetadata
,
252 uint32_t aMetaOrigLen
)
254 if (!aFontEntry
->mUserFontData
) {
255 aFontEntry
->mUserFontData
= new gfxUserFontData
;
257 gfxUserFontData
* userFontData
= aFontEntry
->mUserFontData
;
258 userFontData
->mSrcIndex
= mSrcIndex
;
259 const gfxFontFaceSrc
& src
= mSrcList
[mSrcIndex
];
261 userFontData
->mLocalName
= src
.mLocalName
;
263 userFontData
->mURI
= src
.mURI
;
264 userFontData
->mPrincipal
= mPrincipal
;
266 userFontData
->mPrivate
= aPrivate
;
267 userFontData
->mFormat
= src
.mFormatFlags
;
268 userFontData
->mRealName
= aOriginalName
;
270 userFontData
->mMetadata
.SwapElements(*aMetadata
);
271 userFontData
->mMetaOrigLen
= aMetaOrigLen
;
276 AutoSwap_PRUint32 signature
;
277 AutoSwap_PRUint32 flavor
;
278 AutoSwap_PRUint32 length
;
279 AutoSwap_PRUint16 numTables
;
280 AutoSwap_PRUint16 reserved
;
281 AutoSwap_PRUint32 totalSfntSize
;
282 AutoSwap_PRUint16 majorVersion
;
283 AutoSwap_PRUint16 minorVersion
;
284 AutoSwap_PRUint32 metaOffset
;
285 AutoSwap_PRUint32 metaCompLen
;
286 AutoSwap_PRUint32 metaOrigLen
;
287 AutoSwap_PRUint32 privOffset
;
288 AutoSwap_PRUint32 privLen
;
292 CopyWOFFMetadata(const uint8_t* aFontData
,
294 FallibleTArray
<uint8_t>* aMetadata
,
295 uint32_t* aMetaOrigLen
)
297 // This function may be called with arbitrary, unvalidated "font" data
298 // from @font-face, so it needs to be careful to bounds-check, etc.,
299 // before trying to read anything.
300 // This just saves a copy of the compressed data block; it does NOT check
301 // that the block can be successfully decompressed, or that it contains
302 // well-formed/valid XML metadata.
303 if (aLength
< sizeof(WOFFHeader
)) {
306 const WOFFHeader
* woff
= reinterpret_cast<const WOFFHeader
*>(aFontData
);
307 uint32_t metaOffset
= woff
->metaOffset
;
308 uint32_t metaCompLen
= woff
->metaCompLen
;
309 if (!metaOffset
|| !metaCompLen
|| !woff
->metaOrigLen
) {
312 if (metaOffset
>= aLength
|| metaCompLen
> aLength
- metaOffset
) {
315 if (!aMetadata
->SetLength(woff
->metaCompLen
)) {
318 memcpy(aMetadata
->Elements(), aFontData
+ metaOffset
, metaCompLen
);
319 *aMetaOrigLen
= woff
->metaOrigLen
;
322 gfxProxyFontEntry::LoadStatus
323 gfxProxyFontEntry::LoadNext(gfxMixedFontFamily
*aFamily
,
324 bool& aLocalRulesUsed
)
326 uint32_t numSrc
= mSrcList
.Length();
328 NS_ASSERTION(mSrcIndex
< numSrc
,
329 "already at the end of the src list for user font");
331 if (mLoadingState
== NOT_LOADING
) {
332 mLoadingState
= LOADING_STARTED
;
333 mUnsupportedFormat
= false;
335 // we were already loading; move to the next source,
336 // but don't reset state - if we've already timed out,
337 // that counts against the new download
341 // load each src entry in turn, until a local face is found
342 // or a download begins successfully
343 while (mSrcIndex
< numSrc
) {
344 const gfxFontFaceSrc
& currSrc
= mSrcList
[mSrcIndex
];
346 // src local ==> lookup and load immediately
348 if (currSrc
.mIsLocal
) {
350 gfxPlatform::GetPlatform()->LookupLocalFont(this,
352 aLocalRulesUsed
= true;
354 LOG(("fontset (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
356 NS_ConvertUTF16toUTF8(currSrc
.mLocalName
).get(),
357 NS_ConvertUTF16toUTF8(mFamilyName
).get(),
358 uint32_t(mFontSet
->mGeneration
)));
359 fe
->mFeatureSettings
.AppendElements(mFeatureSettings
);
360 fe
->mLanguageOverride
= mLanguageOverride
;
361 fe
->mFamilyName
= mFamilyName
;
362 // For src:local(), we don't care whether the request is from
363 // a private window as there's no issue of caching resources;
364 // local fonts are just available all the time.
365 StoreUserFontData(fe
, false, nsString(), nullptr, 0);
366 mFontSet
->ReplaceFontEntry(aFamily
, this, fe
);
367 return STATUS_LOADED
;
369 LOG(("fontset (%p) [src %d] failed local: (%s) for (%s)\n",
371 NS_ConvertUTF16toUTF8(currSrc
.mLocalName
).get(),
372 NS_ConvertUTF16toUTF8(mFamilyName
).get()));
376 // src url ==> start the load process
378 if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc
.mURI
,
379 currSrc
.mFormatFlags
)) {
381 nsIPrincipal
*principal
= nullptr;
383 nsresult rv
= mFontSet
->CheckFontLoad(&currSrc
, &principal
,
386 if (NS_SUCCEEDED(rv
) && principal
!= nullptr) {
388 // see if we have an existing entry for this source
389 gfxFontEntry
*fe
= gfxUserFontSet::
390 UserFontCache::GetFont(currSrc
.mURI
,
393 mFontSet
->GetPrivateBrowsing());
395 mFontSet
->ReplaceFontEntry(aFamily
, this, fe
);
396 return STATUS_LOADED
;
400 // record the principal returned by CheckFontLoad,
401 // for use when creating a channel
402 // and when caching the loaded entry
403 mPrincipal
= principal
;
405 bool loadDoesntSpin
= false;
406 rv
= NS_URIChainHasFlags(currSrc
.mURI
,
407 nsIProtocolHandler::URI_SYNC_LOAD_IS_OK
,
410 if (NS_SUCCEEDED(rv
) && loadDoesntSpin
) {
411 uint8_t *buffer
= nullptr;
412 uint32_t bufferLength
= 0;
414 // sync load font immediately
415 rv
= mFontSet
->SyncLoadFontData(this, &currSrc
, buffer
,
418 if (NS_SUCCEEDED(rv
) &&
419 LoadFont(aFamily
, buffer
, bufferLength
)) {
420 return STATUS_LOADED
;
422 mFontSet
->LogMessage(aFamily
, this,
424 nsIScriptError::errorFlag
,
429 // otherwise load font async
430 rv
= mFontSet
->StartLoad(aFamily
, this, &currSrc
);
431 bool loadOK
= NS_SUCCEEDED(rv
);
436 nsAutoCString fontURI
;
437 currSrc
.mURI
->GetSpec(fontURI
);
438 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
439 mFontSet
, mSrcIndex
, fontURI
.get(),
440 NS_ConvertUTF16toUTF8(mFamilyName
).get()));
443 return STATUS_LOADING
;
445 mFontSet
->LogMessage(aFamily
, this,
447 nsIScriptError::errorFlag
,
452 mFontSet
->LogMessage(aFamily
, this, "download not allowed",
453 nsIScriptError::errorFlag
, rv
);
456 // We don't log a warning to the web console yet,
457 // as another source may load successfully
458 mUnsupportedFormat
= true;
465 if (mUnsupportedFormat
) {
466 mFontSet
->LogMessage(aFamily
, this, "no supported format found",
467 nsIScriptError::warningFlag
);
470 // all src's failed; mark this entry as unusable (so fallback will occur)
471 LOG(("userfonts (%p) failed all src for (%s)\n",
472 mFontSet
, NS_ConvertUTF16toUTF8(mFamilyName
).get()));
473 mLoadingState
= LOADING_FAILED
;
475 return STATUS_END_OF_LIST
;
479 gfxProxyFontEntry::LoadFont(gfxMixedFontFamily
*aFamily
,
480 const uint8_t *aFontData
, uint32_t &aLength
)
482 gfxFontEntry
*fe
= nullptr;
484 gfxUserFontType fontType
=
485 gfxFontUtils::DetermineFontDataType(aFontData
, aLength
);
487 // Unwrap/decompress/sanitize or otherwise munge the downloaded data
488 // to make a usable sfnt structure.
490 // Because platform font activation code may replace the name table
491 // in the font with a synthetic one, we save the original name so that
492 // it can be reported via the nsIDOMFontFace API.
493 nsAutoString originalFullName
;
495 // Call the OTS sanitizer; this will also decode WOFF to sfnt
496 // if necessary. The original data in aFontData is left unchanged.
498 const uint8_t* saneData
=
499 SanitizeOpenTypeData(aFamily
, aFontData
, aLength
, saneLen
,
500 fontType
== GFX_USERFONT_WOFF
);
502 mFontSet
->LogMessage(aFamily
, this, "rejected by sanitizer");
505 // The sanitizer ensures that we have a valid sfnt and a usable
506 // name table, so this should never fail unless we're out of
507 // memory, and GetFullNameFromSFNT is not directly exposed to
508 // arbitrary/malicious data from the web.
509 gfxFontUtils::GetFullNameFromSFNT(saneData
, saneLen
,
511 // Here ownership of saneData is passed to the platform,
512 // which will delete it when no longer required
513 fe
= gfxPlatform::GetPlatform()->MakePlatformFont(this,
517 mFontSet
->LogMessage(aFamily
, this, "not usable by platform");
522 // Save a copy of the metadata block (if present) for nsIDOMFontFace
523 // to use if required. Ownership of the metadata block will be passed
524 // to the gfxUserFontData record below.
525 FallibleTArray
<uint8_t> metadata
;
526 uint32_t metaOrigLen
= 0;
527 if (fontType
== GFX_USERFONT_WOFF
) {
528 CopyWOFFMetadata(aFontData
, aLength
, &metadata
, &metaOrigLen
);
531 // copy OpenType feature/language settings from the proxy to the
532 // newly-created font entry
533 fe
->mFeatureSettings
.AppendElements(mFeatureSettings
);
534 fe
->mLanguageOverride
= mLanguageOverride
;
535 fe
->mFamilyName
= mFamilyName
;
536 StoreUserFontData(fe
, mFontSet
->GetPrivateBrowsing(), originalFullName
,
537 &metadata
, metaOrigLen
);
540 nsAutoCString fontURI
;
541 mSrcList
[mSrcIndex
].mURI
->GetSpec(fontURI
);
542 LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
543 this, mSrcIndex
, fontURI
.get(),
544 NS_ConvertUTF16toUTF8(mFamilyName
).get(),
545 uint32_t(mFontSet
->mGeneration
)));
548 mFontSet
->ReplaceFontEntry(aFamily
, this, fe
);
549 gfxUserFontSet::UserFontCache::CacheFont(fe
);
553 nsAutoCString fontURI
;
554 mSrcList
[mSrcIndex
].mURI
->GetSpec(fontURI
);
555 LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
556 " error making platform font\n",
557 this, mSrcIndex
, fontURI
.get(),
558 NS_ConvertUTF16toUTF8(mFamilyName
).get()));
563 // The downloaded data can now be discarded; the font entry is using the
565 NS_Free((void*)aFontData
);
570 gfxUserFontSet::gfxUserFontSet()
571 : mFontFamilies(4), mLocalRulesUsed(false)
573 IncrementGeneration();
574 gfxPlatformFontList
*fp
= gfxPlatformFontList::PlatformFontList();
576 fp
->AddUserFontSet(this);
580 gfxUserFontSet::~gfxUserFontSet()
582 gfxPlatformFontList
*fp
= gfxPlatformFontList::PlatformFontList();
584 fp
->RemoveUserFontSet(this);
588 already_AddRefed
<gfxProxyFontEntry
>
589 gfxUserFontSet::FindOrCreateFontFace(
590 const nsAString
& aFamilyName
,
591 const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
594 uint32_t aItalicStyle
,
595 const nsTArray
<gfxFontFeature
>& aFeatureSettings
,
596 uint32_t aLanguageOverride
,
597 gfxSparseBitSet
* aUnicodeRanges
)
599 nsRefPtr
<gfxProxyFontEntry
> entry
;
601 // If there's already a proxy in the family whose descriptors all match,
602 // we can just move it to the end of the list instead of adding a new
603 // face that will always "shadow" the old one.
604 // Note that we can't do this for "real" (non-proxy) entries, even if the
605 // style descriptors match, as they might have had a different source list,
606 // but we no longer have the old source list available to check.
607 gfxMixedFontFamily
* family
= LookupFamily(aFamilyName
);
609 entry
= FindExistingProxyEntry(family
, aFontFaceSrcList
, aWeight
,
610 aStretch
, aItalicStyle
, aFeatureSettings
,
611 aLanguageOverride
, aUnicodeRanges
);
615 entry
= CreateFontFace(aFontFaceSrcList
, aWeight
, aStretch
,
616 aItalicStyle
, aFeatureSettings
, aLanguageOverride
,
618 entry
->mFamilyName
= aFamilyName
;
622 LOG(("userfonts (%p) created \"%s\" (%p) with style: %s weight: %d "
624 this, NS_ConvertUTF16toUTF8(aFamilyName
).get(), entry
.get(),
625 (aItalicStyle
& NS_FONT_STYLE_ITALIC
? "italic" :
626 (aItalicStyle
& NS_FONT_STYLE_OBLIQUE
? "oblique" : "normal")),
632 return entry
.forget();
635 already_AddRefed
<gfxProxyFontEntry
>
636 gfxUserFontSet::CreateFontFace(const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
639 uint32_t aItalicStyle
,
640 const nsTArray
<gfxFontFeature
>& aFeatureSettings
,
641 uint32_t aLanguageOverride
,
642 gfxSparseBitSet
* aUnicodeRanges
)
644 MOZ_ASSERT(aWeight
!= 0,
645 "aWeight must not be 0; use NS_FONT_WEIGHT_NORMAL instead");
647 nsRefPtr
<gfxProxyFontEntry
> proxyEntry
=
648 new gfxProxyFontEntry(this, aFontFaceSrcList
, aWeight
,
649 aStretch
, aItalicStyle
, aFeatureSettings
,
650 aLanguageOverride
, aUnicodeRanges
);
651 return proxyEntry
.forget();
655 gfxUserFontSet::FindExistingProxyEntry(
656 gfxMixedFontFamily
* aFamily
,
657 const nsTArray
<gfxFontFaceSrc
>& aFontFaceSrcList
,
660 uint32_t aItalicStyle
,
661 const nsTArray
<gfxFontFeature
>& aFeatureSettings
,
662 uint32_t aLanguageOverride
,
663 gfxSparseBitSet
* aUnicodeRanges
)
665 MOZ_ASSERT(aWeight
!= 0,
666 "aWeight must not be 0; use NS_FONT_WEIGHT_NORMAL instead");
668 nsTArray
<nsRefPtr
<gfxFontEntry
>>& fontList
= aFamily
->GetFontList();
670 for (size_t i
= 0, count
= fontList
.Length(); i
< count
; i
++) {
671 if (!fontList
[i
]->mIsProxy
) {
675 gfxProxyFontEntry
* existingProxyEntry
=
676 static_cast<gfxProxyFontEntry
*>(fontList
[i
].get());
677 if (!existingProxyEntry
->Matches(aFontFaceSrcList
,
678 aWeight
, aStretch
, aItalicStyle
,
679 aFeatureSettings
, aLanguageOverride
,
684 return existingProxyEntry
;
691 gfxUserFontSet::AddFontFace(const nsAString
& aFamilyName
,
692 gfxFontEntry
*aFontEntry
)
694 gfxMixedFontFamily
* family
= GetFamily(aFamilyName
);
695 family
->AddFontEntry(aFontEntry
);
699 LOG(("userfonts (%p) added \"%s\" (%p)",
700 this, NS_ConvertUTF16toUTF8(aFamilyName
).get(), aFontEntry
));
706 gfxUserFontSet::FindFontEntry(gfxFontFamily
*aFamily
,
707 const gfxFontStyle
& aFontStyle
,
709 bool& aWaitForUserFont
)
711 aWaitForUserFont
= false;
712 gfxMixedFontFamily
*family
= static_cast<gfxMixedFontFamily
*>(aFamily
);
714 gfxFontEntry
* fe
= family
->FindFontForStyle(aFontStyle
, aNeedsBold
);
716 // if not a proxy, font has already been loaded
721 gfxProxyFontEntry
*proxyEntry
= static_cast<gfxProxyFontEntry
*> (fe
);
723 // if currently loading, return null for now
724 if (proxyEntry
->mLoadingState
> gfxProxyFontEntry::NOT_LOADING
) {
726 (proxyEntry
->mLoadingState
< gfxProxyFontEntry::LOADING_SLOWLY
);
730 // hasn't been loaded yet, start the load process
731 gfxProxyFontEntry::LoadStatus status
;
733 // NOTE that if all sources in the entry fail, this will delete proxyEntry,
734 // so we cannot use it again if status==STATUS_END_OF_LIST
735 status
= proxyEntry
->LoadNext(family
, mLocalRulesUsed
);
737 // if the load succeeded immediately, the font entry was replaced so
739 if (status
== gfxProxyFontEntry::STATUS_LOADED
) {
740 return family
->FindFontForStyle(aFontStyle
, aNeedsBold
);
743 // check whether we should wait for load to complete before painting
744 // a fallback font -- but not if all sources failed (bug 633500)
745 aWaitForUserFont
= (status
!= gfxProxyFontEntry::STATUS_END_OF_LIST
) &&
746 (proxyEntry
->mLoadingState
< gfxProxyFontEntry::LOADING_SLOWLY
);
748 // if either loading or an error occurred, return null
752 // This is called when a font download finishes.
753 // Ownership of aFontData passes in here, and the font set must
754 // ensure that it is eventually deleted via NS_Free().
756 gfxUserFontSet::OnLoadComplete(gfxMixedFontFamily
*aFamily
,
757 gfxProxyFontEntry
*aProxy
,
758 const uint8_t *aFontData
, uint32_t aLength
,
759 nsresult aDownloadStatus
)
761 // forget about the loader, as we no longer potentially need to cancel it
762 // if the entry is obsoleted
763 aProxy
->mLoader
= nullptr;
765 // download successful, make platform font using font data
766 if (NS_SUCCEEDED(aDownloadStatus
)) {
767 gfxFontEntry
*fe
= aProxy
->LoadFont(aFamily
, aFontData
, aLength
);
771 IncrementGeneration();
777 LogMessage(aFamily
, aProxy
,
778 "download failed", nsIScriptError::errorFlag
,
783 moz_free((void*)aFontData
);
786 // error occurred, load next src
787 (void)aProxy
->LoadNext(aFamily
, mLocalRulesUsed
);
789 // We ignore the status returned by LoadNext();
790 // even if loading failed, we need to bump the font-set generation
791 // and return true in order to trigger reflow, so that fallback
792 // will be used where the text was "masked" by the pending download
793 IncrementGeneration();
798 gfxUserFontSet::IncrementGeneration()
800 // add one, increment again if zero
801 ++sFontSetGeneration
;
802 if (sFontSetGeneration
== 0)
803 ++sFontSetGeneration
;
804 mGeneration
= sFontSetGeneration
;
808 gfxUserFontSet::RebuildLocalRules()
810 if (mLocalRulesUsed
) {
811 DoRebuildUserFontSet();
816 gfxUserFontSet::LookupFamily(const nsAString
& aFamilyName
) const
818 nsAutoString
key(aFamilyName
);
821 return mFontFamilies
.GetWeak(key
);
825 gfxUserFontSet::GetFamily(const nsAString
& aFamilyName
)
827 nsAutoString
key(aFamilyName
);
830 gfxMixedFontFamily
* family
= mFontFamilies
.GetWeak(key
);
832 family
= new gfxMixedFontFamily(aFamilyName
);
833 mFontFamilies
.Put(key
, family
);
838 struct FindFamilyCallbackData
{
839 gfxFontEntry
*mFontEntry
;
840 gfxFontFamily
*mFamily
;
843 static PLDHashOperator
844 FindFamilyCallback(const nsAString
& aName
,
845 gfxMixedFontFamily
* aFamily
,
848 FindFamilyCallbackData
*d
= static_cast<FindFamilyCallbackData
*>(aUserArg
);
849 if (aFamily
->ContainsFace(d
->mFontEntry
)) {
850 d
->mFamily
= aFamily
;
851 return PL_DHASH_STOP
;
854 return PL_DHASH_NEXT
;
858 gfxUserFontSet::FindFamilyFor(gfxFontEntry
* aFontEntry
) const
860 FindFamilyCallbackData d
= { aFontEntry
, nullptr };
861 mFontFamilies
.EnumerateRead(FindFamilyCallback
, &d
);
865 ///////////////////////////////////////////////////////////////////////////////
866 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
867 // across pages/fontsets rather than instantiating new platform fonts.
869 // Entries are added to this cache when a platform font is instantiated from
870 // downloaded data, and removed when the platform font entry is destroyed.
871 // We don't need to use a timed expiration scheme here because the gfxFontEntry
872 // for a downloaded font will be kept alive by its corresponding gfxFont
873 // instance(s) until they are deleted, and *that* happens using an expiration
874 // tracker (gfxFontCache). The result is that the downloaded font instances
875 // recorded here will persist between pages and can get reused (provided the
876 // source URI and principal match, of course).
877 ///////////////////////////////////////////////////////////////////////////////
879 nsTHashtable
<gfxUserFontSet::UserFontCache::Entry
>*
880 gfxUserFontSet::UserFontCache::sUserFonts
= nullptr;
882 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher
, nsIObserver
)
885 gfxUserFontSet::UserFontCache::Entry::RemoveUnlessPersistent(Entry
* aEntry
,
888 return aEntry
->mPersistence
== kPersistent
? PL_DHASH_NEXT
:
893 gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry
* aEntry
,
896 return aEntry
->mPrivate
? PL_DHASH_REMOVE
: PL_DHASH_NEXT
;
900 gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches(Entry
* aEntry
,
903 return aEntry
->GetFontEntry() == static_cast<gfxFontEntry
*>(aUserData
) ?
904 PL_DHASH_REMOVE
: PL_DHASH_NEXT
;
908 gfxUserFontSet::UserFontCache::Entry::DisconnectSVG(Entry
* aEntry
,
911 aEntry
->GetFontEntry()->DisconnectSVG();
912 return PL_DHASH_NEXT
;
916 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports
* aSubject
,
918 const char16_t
* aData
)
924 if (!strcmp(aTopic
, "cacheservice:empty-cache")) {
925 sUserFonts
->EnumerateEntries(Entry::RemoveUnlessPersistent
, nullptr);
926 } else if (!strcmp(aTopic
, "last-pb-context-exited")) {
927 sUserFonts
->EnumerateEntries(Entry::RemoveIfPrivate
, nullptr);
928 } else if (!strcmp(aTopic
, "xpcom-shutdown")) {
929 sUserFonts
->EnumerateEntries(Entry::DisconnectSVG
, nullptr);
931 NS_NOTREACHED("unexpected topic");
938 IgnorePrincipal(nsIURI
*aURI
)
941 bool inherits
= false;
942 rv
= NS_URIChainHasFlags(aURI
,
943 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT
,
945 return NS_SUCCEEDED(rv
) && inherits
;
949 gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey
) const
951 const gfxFontEntry
*fe
= aKey
->mFontEntry
;
952 // CRC32 checking mode
953 if (mLength
|| aKey
->mLength
) {
954 if (aKey
->mLength
!= mLength
||
955 aKey
->mCRC32
!= mCRC32
) {
960 if (NS_FAILED(mURI
->Equals(aKey
->mURI
, &result
)) || !result
) {
964 // For data: URIs, we don't care about the principal; otherwise, check it.
965 if (!IgnorePrincipal(mURI
)) {
966 NS_ASSERTION(mPrincipal
&& aKey
->mPrincipal
,
967 "only data: URIs are allowed to omit the principal");
968 if (NS_FAILED(mPrincipal
->Equals(aKey
->mPrincipal
, &result
)) ||
974 if (mPrivate
!= aKey
->mPrivate
) {
979 if (mFontEntry
->mItalic
!= fe
->mItalic
||
980 mFontEntry
->mWeight
!= fe
->mWeight
||
981 mFontEntry
->mStretch
!= fe
->mStretch
||
982 mFontEntry
->mFeatureSettings
!= fe
->mFeatureSettings
||
983 mFontEntry
->mLanguageOverride
!= fe
->mLanguageOverride
||
984 mFontEntry
->mFamilyName
!= fe
->mFamilyName
) {
992 gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry
*aFontEntry
,
993 EntryPersistence aPersistence
)
995 NS_ASSERTION(aFontEntry
->mFamilyName
.Length() != 0,
996 "caching a font associated with no family yet");
998 sUserFonts
= new nsTHashtable
<Entry
>;
1000 nsCOMPtr
<nsIObserverService
> obs
=
1001 mozilla::services::GetObserverService();
1003 Flusher
*flusher
= new Flusher
;
1004 obs
->AddObserver(flusher
, "cacheservice:empty-cache",
1006 obs
->AddObserver(flusher
, "last-pb-context-exited", false);
1007 obs
->AddObserver(flusher
, "xpcom-shutdown", false);
1011 gfxUserFontData
*data
= aFontEntry
->mUserFontData
;
1012 if (data
->mLength
) {
1013 MOZ_ASSERT(aPersistence
== kPersistent
);
1014 MOZ_ASSERT(!data
->mPrivate
);
1015 sUserFonts
->PutEntry(Key(data
->mCRC32
, data
->mLength
, aFontEntry
,
1016 data
->mPrivate
, aPersistence
));
1018 MOZ_ASSERT(aPersistence
== kDiscardable
);
1019 // For data: URIs, the principal is ignored; anyone who has the same
1020 // data: URI is able to load it and get an equivalent font.
1021 // Otherwise, the principal is used as part of the cache key.
1022 nsIPrincipal
*principal
;
1023 if (IgnorePrincipal(data
->mURI
)) {
1024 principal
= nullptr;
1026 principal
= data
->mPrincipal
;
1028 sUserFonts
->PutEntry(Key(data
->mURI
, principal
, aFontEntry
,
1029 data
->mPrivate
, aPersistence
));
1032 #ifdef DEBUG_USERFONT_CACHE
1033 printf("userfontcache added fontentry: %p\n", aFontEntry
);
1039 gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry
*aFontEntry
)
1042 // if we've already deleted the cache (i.e. during shutdown),
1047 // We can't simply use RemoveEntry here because it's possible the principal
1048 // may have changed since the font was cached, in which case the lookup
1049 // would no longer find the entry (bug 838105).
1050 sUserFonts
->EnumerateEntries(
1051 gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches
, aFontEntry
);
1053 #ifdef DEBUG_USERFONT_CACHE
1054 printf("userfontcache removed fontentry: %p\n", aFontEntry
);
1060 gfxUserFontSet::UserFontCache::GetFont(nsIURI
*aSrcURI
,
1061 nsIPrincipal
*aPrincipal
,
1062 gfxProxyFontEntry
*aProxy
,
1069 // Ignore principal when looking up a data: URI.
1070 nsIPrincipal
*principal
;
1071 if (IgnorePrincipal(aSrcURI
)) {
1072 principal
= nullptr;
1074 principal
= aPrincipal
;
1077 Entry
* entry
= sUserFonts
->GetEntry(Key(aSrcURI
, principal
, aProxy
,
1080 return entry
->GetFontEntry();
1083 nsCOMPtr
<nsIChannel
> chan
;
1084 if (NS_FAILED(NS_NewChannel(getter_AddRefs(chan
), aSrcURI
))) {
1088 nsCOMPtr
<nsIJARChannel
> jarchan
= do_QueryInterface(chan
);
1093 nsCOMPtr
<nsIZipEntry
> zipentry
;
1094 if (NS_FAILED(jarchan
->GetZipEntry(getter_AddRefs(zipentry
)))) {
1098 uint32_t crc32
, length
;
1099 zipentry
->GetCRC32(&crc32
);
1100 zipentry
->GetRealSize(&length
);
1102 entry
= sUserFonts
->GetEntry(Key(crc32
, length
, aProxy
, aPrivate
));
1104 return entry
->GetFontEntry();
1111 gfxUserFontSet::UserFontCache::Shutdown()
1115 sUserFonts
= nullptr;
1119 #ifdef DEBUG_USERFONT_CACHE
1122 gfxUserFontSet::UserFontCache::Entry::DumpEntry(Entry
* aEntry
, void* aUserData
)
1126 nsAutoCString
principalURISpec("(null)");
1127 bool setDomain
= false;
1129 if (aEntry
->mPrincipal
) {
1130 nsCOMPtr
<nsIURI
> principalURI
;
1131 rv
= aEntry
->mPrincipal
->GetURI(getter_AddRefs(principalURI
));
1132 if (NS_SUCCEEDED(rv
)) {
1133 principalURI
->GetSpec(principalURISpec
);
1136 nsCOMPtr
<nsIURI
> domainURI
;
1137 aEntry
->mPrincipal
->GetDomain(getter_AddRefs(domainURI
));
1143 NS_ASSERTION(aEntry
->mURI
, "null URI in userfont cache entry");
1145 printf("userfontcache fontEntry: %p fonturihash: %8.8x family: %s domainset: %s principal: [%s]\n",
1147 nsURIHashKey::HashKey(aEntry
->mURI
),
1148 NS_ConvertUTF16toUTF8(aEntry
->mFontEntry
->FamilyName()).get(),
1149 (setDomain
? "true" : "false"),
1150 principalURISpec
.get()
1152 return PL_DHASH_NEXT
;
1156 gfxUserFontSet::UserFontCache::Dump()
1162 printf("userfontcache dump count: %d ========\n", sUserFonts
->Count());
1163 sUserFonts
->EnumerateEntries(Entry::DumpEntry
, nullptr);
1164 printf("userfontcache dump ==================\n");