1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxUserFontSet.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1056 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifdef MOZ_LOGGING 1.10 +#define FORCE_PR_LOG /* Allow logging in the release build */ 1.11 +#endif /* MOZ_LOGGING */ 1.12 +#include "prlog.h" 1.13 + 1.14 +#include "gfxUserFontSet.h" 1.15 +#include "nsFont.h" 1.16 +#include "gfxPlatform.h" 1.17 +#include "nsUnicharUtils.h" 1.18 +#include "nsNetUtil.h" 1.19 +#include "nsICacheService.h" 1.20 +#include "nsIProtocolHandler.h" 1.21 +#include "nsIPrincipal.h" 1.22 +#include "gfxFontConstants.h" 1.23 +#include "mozilla/Services.h" 1.24 +#include "mozilla/gfx/2D.h" 1.25 +#include "gfxPlatformFontList.h" 1.26 + 1.27 +#include "opentype-sanitiser.h" 1.28 +#include "ots-memory-stream.h" 1.29 + 1.30 +using namespace mozilla; 1.31 + 1.32 +#ifdef PR_LOGGING 1.33 +PRLogModuleInfo * 1.34 +gfxUserFontSet::GetUserFontsLog() 1.35 +{ 1.36 + static PRLogModuleInfo *sLog; 1.37 + if (!sLog) 1.38 + sLog = PR_NewLogModule("userfonts"); 1.39 + return sLog; 1.40 +} 1.41 +#endif /* PR_LOGGING */ 1.42 + 1.43 +#define LOG(args) PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG, args) 1.44 +#define LOG_ENABLED() PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG) 1.45 + 1.46 +static uint64_t sFontSetGeneration = 0; 1.47 + 1.48 +// TODO: support for unicode ranges not yet implemented 1.49 + 1.50 +gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 1.51 + uint32_t aWeight, 1.52 + int32_t aStretch, 1.53 + uint32_t aItalicStyle, 1.54 + const nsTArray<gfxFontFeature>& aFeatureSettings, 1.55 + uint32_t aLanguageOverride, 1.56 + gfxSparseBitSet *aUnicodeRanges) 1.57 + : gfxFontEntry(NS_LITERAL_STRING("Proxy")), 1.58 + mLoadingState(NOT_LOADING), 1.59 + mUnsupportedFormat(false), 1.60 + mLoader(nullptr) 1.61 +{ 1.62 + mIsProxy = true; 1.63 + mSrcList = aFontFaceSrcList; 1.64 + mSrcIndex = 0; 1.65 + mWeight = aWeight; 1.66 + mStretch = aStretch; 1.67 + // XXX Currently, we don't distinguish 'italic' and 'oblique' styles; 1.68 + // we need to fix this. (Bug 543715) 1.69 + mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; 1.70 + mFeatureSettings.AppendElements(aFeatureSettings); 1.71 + mLanguageOverride = aLanguageOverride; 1.72 + mIsUserFont = true; 1.73 +} 1.74 + 1.75 +gfxProxyFontEntry::~gfxProxyFontEntry() 1.76 +{ 1.77 +} 1.78 + 1.79 +bool 1.80 +gfxProxyFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 1.81 + uint32_t aWeight, 1.82 + int32_t aStretch, 1.83 + uint32_t aItalicStyle, 1.84 + const nsTArray<gfxFontFeature>& aFeatureSettings, 1.85 + uint32_t aLanguageOverride, 1.86 + gfxSparseBitSet *aUnicodeRanges) 1.87 +{ 1.88 + // XXX font entries don't distinguish italic from oblique (bug 543715) 1.89 + bool isItalic = 1.90 + (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; 1.91 + 1.92 + return mWeight == aWeight && 1.93 + mStretch == aStretch && 1.94 + mItalic == isItalic && 1.95 + mFeatureSettings == aFeatureSettings && 1.96 + mLanguageOverride == aLanguageOverride && 1.97 + mSrcList == aFontFaceSrcList; 1.98 + // XXX once we support unicode-range (bug 475891), 1.99 + // we'll need to compare that here as well 1.100 +} 1.101 + 1.102 +gfxFont* 1.103 +gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) 1.104 +{ 1.105 + // cannot create an actual font for a proxy entry 1.106 + return nullptr; 1.107 +} 1.108 + 1.109 +gfxUserFontSet::gfxUserFontSet() 1.110 + : mFontFamilies(5), mLocalRulesUsed(false) 1.111 +{ 1.112 + IncrementGeneration(); 1.113 + gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); 1.114 + if (fp) { 1.115 + fp->AddUserFontSet(this); 1.116 + } 1.117 +} 1.118 + 1.119 +gfxUserFontSet::~gfxUserFontSet() 1.120 +{ 1.121 + gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); 1.122 + if (fp) { 1.123 + fp->RemoveUserFontSet(this); 1.124 + } 1.125 +} 1.126 + 1.127 +gfxFontEntry* 1.128 +gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, 1.129 + const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, 1.130 + uint32_t aWeight, 1.131 + int32_t aStretch, 1.132 + uint32_t aItalicStyle, 1.133 + const nsTArray<gfxFontFeature>& aFeatureSettings, 1.134 + const nsString& aLanguageOverride, 1.135 + gfxSparseBitSet *aUnicodeRanges) 1.136 +{ 1.137 + nsAutoString key(aFamilyName); 1.138 + ToLowerCase(key); 1.139 + 1.140 + bool found; 1.141 + 1.142 + if (aWeight == 0) 1.143 + aWeight = NS_FONT_WEIGHT_NORMAL; 1.144 + 1.145 + // stretch, italic/oblique ==> zero implies normal 1.146 + 1.147 + gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found); 1.148 + if (!family) { 1.149 + family = new gfxMixedFontFamily(aFamilyName); 1.150 + mFontFamilies.Put(key, family); 1.151 + } 1.152 + 1.153 + uint32_t languageOverride = 1.154 + gfxFontStyle::ParseFontLanguageOverride(aLanguageOverride); 1.155 + 1.156 + // If there's already a proxy in the family whose descriptors all match, 1.157 + // we can just move it to the end of the list instead of adding a new 1.158 + // face that will always "shadow" the old one. 1.159 + // Note that we can't do this for "real" (non-proxy) entries, even if the 1.160 + // style descriptors match, as they might have had a different source list, 1.161 + // but we no longer have the old source list available to check. 1.162 + nsTArray<nsRefPtr<gfxFontEntry> >& fontList = family->GetFontList(); 1.163 + for (uint32_t i = 0, count = fontList.Length(); i < count; i++) { 1.164 + if (!fontList[i]->mIsProxy) { 1.165 + continue; 1.166 + } 1.167 + 1.168 + gfxProxyFontEntry *existingProxyEntry = 1.169 + static_cast<gfxProxyFontEntry*>(fontList[i].get()); 1.170 + if (!existingProxyEntry->Matches(aFontFaceSrcList, 1.171 + aWeight, aStretch, aItalicStyle, 1.172 + aFeatureSettings, languageOverride, 1.173 + aUnicodeRanges)) { 1.174 + continue; 1.175 + } 1.176 + 1.177 + // We've found an entry that matches the new face exactly, so just add 1.178 + // it to the end of the list. gfxMixedFontFamily::AddFontEntry() will 1.179 + // automatically remove any earlier occurrence of the same proxy. 1.180 + family->AddFontEntry(existingProxyEntry); 1.181 + return existingProxyEntry; 1.182 + } 1.183 + 1.184 + // construct a new face and add it into the family 1.185 + gfxProxyFontEntry *proxyEntry = 1.186 + new gfxProxyFontEntry(aFontFaceSrcList, aWeight, aStretch, 1.187 + aItalicStyle, 1.188 + aFeatureSettings, 1.189 + languageOverride, 1.190 + aUnicodeRanges); 1.191 + family->AddFontEntry(proxyEntry); 1.192 +#ifdef PR_LOGGING 1.193 + if (LOG_ENABLED()) { 1.194 + LOG(("userfonts (%p) added (%s) with style: %s weight: %d stretch: %d", 1.195 + this, NS_ConvertUTF16toUTF8(aFamilyName).get(), 1.196 + (aItalicStyle & NS_FONT_STYLE_ITALIC ? "italic" : 1.197 + (aItalicStyle & NS_FONT_STYLE_OBLIQUE ? "oblique" : "normal")), 1.198 + aWeight, aStretch)); 1.199 + } 1.200 +#endif 1.201 + 1.202 + return proxyEntry; 1.203 +} 1.204 + 1.205 +void 1.206 +gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, 1.207 + gfxFontEntry *aFontEntry) 1.208 +{ 1.209 + nsAutoString key(aFamilyName); 1.210 + ToLowerCase(key); 1.211 + 1.212 + bool found; 1.213 + 1.214 + gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found); 1.215 + if (!family) { 1.216 + family = new gfxMixedFontFamily(aFamilyName); 1.217 + mFontFamilies.Put(key, family); 1.218 + } 1.219 + 1.220 + family->AddFontEntry(aFontEntry); 1.221 +} 1.222 + 1.223 +gfxFontEntry* 1.224 +gfxUserFontSet::FindFontEntry(gfxFontFamily *aFamily, 1.225 + const gfxFontStyle& aFontStyle, 1.226 + bool& aNeedsBold, 1.227 + bool& aWaitForUserFont) 1.228 +{ 1.229 + aWaitForUserFont = false; 1.230 + gfxMixedFontFamily *family = static_cast<gfxMixedFontFamily*>(aFamily); 1.231 + 1.232 + gfxFontEntry* fe = family->FindFontForStyle(aFontStyle, aNeedsBold); 1.233 + 1.234 + // if not a proxy, font has already been loaded 1.235 + if (!fe->mIsProxy) { 1.236 + return fe; 1.237 + } 1.238 + 1.239 + gfxProxyFontEntry *proxyEntry = static_cast<gfxProxyFontEntry*> (fe); 1.240 + 1.241 + // if currently loading, return null for now 1.242 + if (proxyEntry->mLoadingState > gfxProxyFontEntry::NOT_LOADING) { 1.243 + aWaitForUserFont = 1.244 + (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY); 1.245 + return nullptr; 1.246 + } 1.247 + 1.248 + // hasn't been loaded yet, start the load process 1.249 + LoadStatus status; 1.250 + 1.251 + // NOTE that if all sources in the entry fail, this will delete proxyEntry, 1.252 + // so we cannot use it again if status==STATUS_END_OF_LIST 1.253 + status = LoadNext(family, proxyEntry); 1.254 + 1.255 + // if the load succeeded immediately, the font entry was replaced so 1.256 + // search again 1.257 + if (status == STATUS_LOADED) { 1.258 + return family->FindFontForStyle(aFontStyle, aNeedsBold); 1.259 + } 1.260 + 1.261 + // check whether we should wait for load to complete before painting 1.262 + // a fallback font -- but not if all sources failed (bug 633500) 1.263 + aWaitForUserFont = (status != STATUS_END_OF_LIST) && 1.264 + (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY); 1.265 + 1.266 + // if either loading or an error occurred, return null 1.267 + return nullptr; 1.268 +} 1.269 + 1.270 +// Based on ots::ExpandingMemoryStream from ots-memory-stream.h, 1.271 +// adapted to use Mozilla allocators and to allow the final 1.272 +// memory buffer to be adopted by the client. 1.273 +class ExpandingMemoryStream : public ots::OTSStream { 1.274 +public: 1.275 + ExpandingMemoryStream(size_t initial, size_t limit) 1.276 + : mLength(initial), mLimit(limit), mOff(0) { 1.277 + mPtr = NS_Alloc(mLength); 1.278 + } 1.279 + 1.280 + ~ExpandingMemoryStream() { 1.281 + NS_Free(mPtr); 1.282 + } 1.283 + 1.284 + // return the buffer, and give up ownership of it 1.285 + // so the caller becomes responsible to call NS_Free 1.286 + // when finished with it 1.287 + void* forget() { 1.288 + void* p = mPtr; 1.289 + mPtr = nullptr; 1.290 + return p; 1.291 + } 1.292 + 1.293 + bool WriteRaw(const void *data, size_t length) { 1.294 + if ((mOff + length > mLength) || 1.295 + (mLength > std::numeric_limits<size_t>::max() - mOff)) { 1.296 + if (mLength == mLimit) { 1.297 + return false; 1.298 + } 1.299 + size_t newLength = (mLength + 1) * 2; 1.300 + if (newLength < mLength) { 1.301 + return false; 1.302 + } 1.303 + if (newLength > mLimit) { 1.304 + newLength = mLimit; 1.305 + } 1.306 + mPtr = NS_Realloc(mPtr, newLength); 1.307 + mLength = newLength; 1.308 + return WriteRaw(data, length); 1.309 + } 1.310 + std::memcpy(static_cast<char*>(mPtr) + mOff, data, length); 1.311 + mOff += length; 1.312 + return true; 1.313 + } 1.314 + 1.315 + bool Seek(off_t position) { 1.316 + if (position < 0) { 1.317 + return false; 1.318 + } 1.319 + if (static_cast<size_t>(position) > mLength) { 1.320 + return false; 1.321 + } 1.322 + mOff = position; 1.323 + return true; 1.324 + } 1.325 + 1.326 + off_t Tell() const { 1.327 + return mOff; 1.328 + } 1.329 + 1.330 +private: 1.331 + void* mPtr; 1.332 + size_t mLength; 1.333 + const size_t mLimit; 1.334 + off_t mOff; 1.335 +}; 1.336 + 1.337 +static ots::TableAction 1.338 +OTSTableAction(uint32_t aTag, void *aUserData) 1.339 +{ 1.340 + // preserve Graphite and SVG tables 1.341 + if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') || 1.342 + aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') || 1.343 + aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') || 1.344 + aTag == TRUETYPE_TAG('G', 'l', 'a', 't') || 1.345 + aTag == TRUETYPE_TAG('F', 'e', 'a', 't') || 1.346 + aTag == TRUETYPE_TAG('S', 'V', 'G', ' ')) { 1.347 + return ots::TABLE_ACTION_PASSTHRU; 1.348 + } 1.349 + return ots::TABLE_ACTION_DEFAULT; 1.350 +} 1.351 + 1.352 +struct OTSCallbackUserData { 1.353 + gfxUserFontSet *mFontSet; 1.354 + gfxMixedFontFamily *mFamily; 1.355 + gfxProxyFontEntry *mProxy; 1.356 +}; 1.357 + 1.358 +/* static */ bool 1.359 +gfxUserFontSet::OTSMessage(void *aUserData, const char *format, ...) 1.360 +{ 1.361 + va_list va; 1.362 + va_start(va, format); 1.363 + 1.364 + // buf should be more than adequate for any message OTS generates, 1.365 + // so we don't worry about checking the result of vsnprintf() 1.366 + char buf[512]; 1.367 + (void)vsnprintf(buf, sizeof(buf), format, va); 1.368 + 1.369 + va_end(va); 1.370 + 1.371 + OTSCallbackUserData *d = static_cast<OTSCallbackUserData*>(aUserData); 1.372 + d->mFontSet->LogMessage(d->mFamily, d->mProxy, buf); 1.373 + 1.374 + return false; 1.375 +} 1.376 + 1.377 +// Call the OTS library to sanitize an sfnt before attempting to use it. 1.378 +// Returns a newly-allocated block, or nullptr in case of fatal errors. 1.379 +const uint8_t* 1.380 +gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily, 1.381 + gfxProxyFontEntry *aProxy, 1.382 + const uint8_t* aData, uint32_t aLength, 1.383 + uint32_t& aSaneLength, bool aIsCompressed) 1.384 +{ 1.385 + // limit output/expansion to 256MB 1.386 + ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength, 1.387 + 1024 * 1024 * 256); 1.388 + 1.389 + OTSCallbackUserData userData; 1.390 + userData.mFontSet = this; 1.391 + userData.mFamily = aFamily; 1.392 + userData.mProxy = aProxy; 1.393 + 1.394 + ots::SetTableActionCallback(&OTSTableAction, nullptr); 1.395 + ots::SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData); 1.396 + 1.397 + if (ots::Process(&output, aData, aLength)) { 1.398 + aSaneLength = output.Tell(); 1.399 + return static_cast<uint8_t*>(output.forget()); 1.400 + } else { 1.401 + aSaneLength = 0; 1.402 + return nullptr; 1.403 + } 1.404 +} 1.405 + 1.406 +static void 1.407 +StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy, 1.408 + bool aPrivate, const nsAString& aOriginalName, 1.409 + FallibleTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen) 1.410 +{ 1.411 + if (!aFontEntry->mUserFontData) { 1.412 + aFontEntry->mUserFontData = new gfxUserFontData; 1.413 + } 1.414 + gfxUserFontData* userFontData = aFontEntry->mUserFontData; 1.415 + userFontData->mSrcIndex = aProxy->mSrcIndex; 1.416 + const gfxFontFaceSrc& src = aProxy->mSrcList[aProxy->mSrcIndex]; 1.417 + if (src.mIsLocal) { 1.418 + userFontData->mLocalName = src.mLocalName; 1.419 + } else { 1.420 + userFontData->mURI = src.mURI; 1.421 + userFontData->mPrincipal = aProxy->mPrincipal; 1.422 + } 1.423 + userFontData->mPrivate = aPrivate; 1.424 + userFontData->mFormat = src.mFormatFlags; 1.425 + userFontData->mRealName = aOriginalName; 1.426 + if (aMetadata) { 1.427 + userFontData->mMetadata.SwapElements(*aMetadata); 1.428 + userFontData->mMetaOrigLen = aMetaOrigLen; 1.429 + } 1.430 +} 1.431 + 1.432 +struct WOFFHeader { 1.433 + AutoSwap_PRUint32 signature; 1.434 + AutoSwap_PRUint32 flavor; 1.435 + AutoSwap_PRUint32 length; 1.436 + AutoSwap_PRUint16 numTables; 1.437 + AutoSwap_PRUint16 reserved; 1.438 + AutoSwap_PRUint32 totalSfntSize; 1.439 + AutoSwap_PRUint16 majorVersion; 1.440 + AutoSwap_PRUint16 minorVersion; 1.441 + AutoSwap_PRUint32 metaOffset; 1.442 + AutoSwap_PRUint32 metaCompLen; 1.443 + AutoSwap_PRUint32 metaOrigLen; 1.444 + AutoSwap_PRUint32 privOffset; 1.445 + AutoSwap_PRUint32 privLen; 1.446 +}; 1.447 + 1.448 +void 1.449 +gfxUserFontSet::CopyWOFFMetadata(const uint8_t* aFontData, 1.450 + uint32_t aLength, 1.451 + FallibleTArray<uint8_t>* aMetadata, 1.452 + uint32_t* aMetaOrigLen) 1.453 +{ 1.454 + // This function may be called with arbitrary, unvalidated "font" data 1.455 + // from @font-face, so it needs to be careful to bounds-check, etc., 1.456 + // before trying to read anything. 1.457 + // This just saves a copy of the compressed data block; it does NOT check 1.458 + // that the block can be successfully decompressed, or that it contains 1.459 + // well-formed/valid XML metadata. 1.460 + if (aLength < sizeof(WOFFHeader)) { 1.461 + return; 1.462 + } 1.463 + const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData); 1.464 + uint32_t metaOffset = woff->metaOffset; 1.465 + uint32_t metaCompLen = woff->metaCompLen; 1.466 + if (!metaOffset || !metaCompLen || !woff->metaOrigLen) { 1.467 + return; 1.468 + } 1.469 + if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) { 1.470 + return; 1.471 + } 1.472 + if (!aMetadata->SetLength(woff->metaCompLen)) { 1.473 + return; 1.474 + } 1.475 + memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen); 1.476 + *aMetaOrigLen = woff->metaOrigLen; 1.477 +} 1.478 + 1.479 +// This is called when a font download finishes. 1.480 +// Ownership of aFontData passes in here, and the font set must 1.481 +// ensure that it is eventually deleted via NS_Free(). 1.482 +bool 1.483 +gfxUserFontSet::OnLoadComplete(gfxMixedFontFamily *aFamily, 1.484 + gfxProxyFontEntry *aProxy, 1.485 + const uint8_t *aFontData, uint32_t aLength, 1.486 + nsresult aDownloadStatus) 1.487 +{ 1.488 + // forget about the loader, as we no longer potentially need to cancel it 1.489 + // if the entry is obsoleted 1.490 + aProxy->mLoader = nullptr; 1.491 + 1.492 + // download successful, make platform font using font data 1.493 + if (NS_SUCCEEDED(aDownloadStatus)) { 1.494 + gfxFontEntry *fe = LoadFont(aFamily, aProxy, aFontData, aLength); 1.495 + aFontData = nullptr; 1.496 + 1.497 + if (fe) { 1.498 + IncrementGeneration(); 1.499 + return true; 1.500 + } 1.501 + 1.502 + } else { 1.503 + // download failed 1.504 + LogMessage(aFamily, aProxy, 1.505 + "download failed", nsIScriptError::errorFlag, 1.506 + aDownloadStatus); 1.507 + } 1.508 + 1.509 + if (aFontData) { 1.510 + NS_Free((void*)aFontData); 1.511 + } 1.512 + 1.513 + // error occurred, load next src 1.514 + (void)LoadNext(aFamily, aProxy); 1.515 + 1.516 + // We ignore the status returned by LoadNext(); 1.517 + // even if loading failed, we need to bump the font-set generation 1.518 + // and return true in order to trigger reflow, so that fallback 1.519 + // will be used where the text was "masked" by the pending download 1.520 + IncrementGeneration(); 1.521 + return true; 1.522 +} 1.523 + 1.524 + 1.525 +gfxUserFontSet::LoadStatus 1.526 +gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily, 1.527 + gfxProxyFontEntry *aProxyEntry) 1.528 +{ 1.529 + uint32_t numSrc = aProxyEntry->mSrcList.Length(); 1.530 + 1.531 + NS_ASSERTION(aProxyEntry->mSrcIndex < numSrc, 1.532 + "already at the end of the src list for user font"); 1.533 + 1.534 + if (aProxyEntry->mLoadingState == gfxProxyFontEntry::NOT_LOADING) { 1.535 + aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_STARTED; 1.536 + aProxyEntry->mUnsupportedFormat = false; 1.537 + } else { 1.538 + // we were already loading; move to the next source, 1.539 + // but don't reset state - if we've already timed out, 1.540 + // that counts against the new download 1.541 + aProxyEntry->mSrcIndex++; 1.542 + } 1.543 + 1.544 + /* If there are any urls, prefer them to local */ 1.545 + bool listHasURL = false; 1.546 + for (uint32_t i = aProxyEntry->mSrcIndex; i < numSrc; i++) { 1.547 + const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[i]; 1.548 + if (!currSrc.mIsLocal) { 1.549 + listHasURL = true; 1.550 + break; 1.551 + } 1.552 + } 1.553 + nsPresContext *pres = GetPresContext(); 1.554 + /* If we have no pres context, simply fail this load */ 1.555 + if (!pres) listHasURL = true; 1.556 + 1.557 + // load each src entry in turn, until a local face is found 1.558 + // or a download begins successfully 1.559 + while (aProxyEntry->mSrcIndex < numSrc) { 1.560 + const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex]; 1.561 + 1.562 + // src local ==> lookup and load immediately 1.563 + 1.564 + if (!listHasURL && currSrc.mIsLocal) { 1.565 + nsFont font; 1.566 + font.name = currSrc.mLocalName; 1.567 + gfxFontEntry *fe = 1.568 + gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry, 1.569 + currSrc.mLocalName); 1.570 + pres->AddFontAttempt(font); 1.571 + 1.572 + /* No more fonts for you */ 1.573 + if (pres->FontAttemptCountReached(font) || 1.574 + pres->FontUseCountReached(font)) { 1.575 + break; 1.576 + } 1.577 + 1.578 + mLocalRulesUsed = true; 1.579 + if (fe) { 1.580 + pres->AddFontUse(font); 1.581 + LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n", 1.582 + this, aProxyEntry->mSrcIndex, 1.583 + NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), 1.584 + NS_ConvertUTF16toUTF8(aFamily->Name()).get(), 1.585 + uint32_t(mGeneration))); 1.586 + fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings); 1.587 + fe->mLanguageOverride = aProxyEntry->mLanguageOverride; 1.588 + // For src:local(), we don't care whether the request is from 1.589 + // a private window as there's no issue of caching resources; 1.590 + // local fonts are just available all the time. 1.591 + StoreUserFontData(fe, aProxyEntry, false, nsString(), nullptr, 0); 1.592 + ReplaceFontEntry(aFamily, aProxyEntry, fe); 1.593 + return STATUS_LOADED; 1.594 + } else { 1.595 + LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", 1.596 + this, aProxyEntry->mSrcIndex, 1.597 + NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), 1.598 + NS_ConvertUTF16toUTF8(aFamily->Name()).get())); 1.599 + } 1.600 + } 1.601 + 1.602 + // src url ==> start the load process 1.603 + else { 1.604 + if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI, 1.605 + currSrc.mFormatFlags)) { 1.606 + 1.607 + nsIPrincipal *principal = nullptr; 1.608 + bool bypassCache; 1.609 + nsresult rv = CheckFontLoad(&currSrc, &principal, &bypassCache); 1.610 + 1.611 + if (NS_SUCCEEDED(rv) && principal != nullptr) { 1.612 + if (!bypassCache) { 1.613 + // see if we have an existing entry for this source 1.614 + gfxFontEntry *fe = 1.615 + UserFontCache::GetFont(currSrc.mURI, principal, 1.616 + aProxyEntry, 1.617 + GetPrivateBrowsing()); 1.618 + if (fe) { 1.619 + ReplaceFontEntry(aFamily, aProxyEntry, fe); 1.620 + return STATUS_LOADED; 1.621 + } 1.622 + } 1.623 + 1.624 + // record the principal returned by CheckFontLoad, 1.625 + // for use when creating a channel 1.626 + // and when caching the loaded entry 1.627 + aProxyEntry->mPrincipal = principal; 1.628 + 1.629 + bool loadDoesntSpin = false; 1.630 + rv = NS_URIChainHasFlags(currSrc.mURI, 1.631 + nsIProtocolHandler::URI_SYNC_LOAD_IS_OK, 1.632 + &loadDoesntSpin); 1.633 + if (NS_SUCCEEDED(rv) && loadDoesntSpin) { 1.634 + uint8_t *buffer = nullptr; 1.635 + uint32_t bufferLength = 0; 1.636 + 1.637 + // sync load font immediately 1.638 + rv = SyncLoadFontData(aProxyEntry, &currSrc, 1.639 + buffer, bufferLength); 1.640 + if (NS_SUCCEEDED(rv) && LoadFont(aFamily, aProxyEntry, 1.641 + buffer, bufferLength)) { 1.642 + return STATUS_LOADED; 1.643 + } else { 1.644 + LogMessage(aFamily, aProxyEntry, 1.645 + "font load failed", 1.646 + nsIScriptError::errorFlag, rv); 1.647 + } 1.648 + } else { 1.649 + // otherwise load font async 1.650 + rv = StartLoad(aFamily, aProxyEntry, &currSrc); 1.651 + if (NS_SUCCEEDED(rv)) { 1.652 +#ifdef PR_LOGGING 1.653 + if (LOG_ENABLED()) { 1.654 + nsAutoCString fontURI; 1.655 + currSrc.mURI->GetSpec(fontURI); 1.656 + LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n", 1.657 + this, aProxyEntry->mSrcIndex, fontURI.get(), 1.658 + NS_ConvertUTF16toUTF8(aFamily->Name()).get())); 1.659 + } 1.660 +#endif 1.661 + return STATUS_LOADING; 1.662 + } else { 1.663 + LogMessage(aFamily, aProxyEntry, 1.664 + "download failed", 1.665 + nsIScriptError::errorFlag, rv); 1.666 + } 1.667 + } 1.668 + } else { 1.669 + LogMessage(aFamily, aProxyEntry, "download not allowed", 1.670 + nsIScriptError::errorFlag, rv); 1.671 + } 1.672 + } else { 1.673 + // We don't log a warning to the web console yet, 1.674 + // as another source may load successfully 1.675 + aProxyEntry->mUnsupportedFormat = true; 1.676 + } 1.677 + } 1.678 + 1.679 + aProxyEntry->mSrcIndex++; 1.680 + } 1.681 + 1.682 + if (aProxyEntry->mUnsupportedFormat) { 1.683 + LogMessage(aFamily, aProxyEntry, "no supported format found", 1.684 + nsIScriptError::warningFlag); 1.685 + } 1.686 + 1.687 + // all src's failed; mark this entry as unusable (so fallback will occur) 1.688 + LOG(("userfonts (%p) failed all src for (%s)\n", 1.689 + this, NS_ConvertUTF16toUTF8(aFamily->Name()).get())); 1.690 + aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_FAILED; 1.691 + 1.692 + return STATUS_END_OF_LIST; 1.693 +} 1.694 + 1.695 +void 1.696 +gfxUserFontSet::IncrementGeneration() 1.697 +{ 1.698 + // add one, increment again if zero 1.699 + ++sFontSetGeneration; 1.700 + if (sFontSetGeneration == 0) 1.701 + ++sFontSetGeneration; 1.702 + mGeneration = sFontSetGeneration; 1.703 +} 1.704 + 1.705 +void 1.706 +gfxUserFontSet::RebuildLocalRules() 1.707 +{ 1.708 + if (mLocalRulesUsed) { 1.709 + DoRebuildUserFontSet(); 1.710 + } 1.711 +} 1.712 + 1.713 +gfxFontEntry* 1.714 +gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily, 1.715 + gfxProxyFontEntry *aProxy, 1.716 + const uint8_t *aFontData, uint32_t &aLength) 1.717 +{ 1.718 + gfxFontEntry *fe = nullptr; 1.719 + 1.720 + gfxUserFontType fontType = 1.721 + gfxFontUtils::DetermineFontDataType(aFontData, aLength); 1.722 + 1.723 + // Unwrap/decompress/sanitize or otherwise munge the downloaded data 1.724 + // to make a usable sfnt structure. 1.725 + 1.726 + // Because platform font activation code may replace the name table 1.727 + // in the font with a synthetic one, we save the original name so that 1.728 + // it can be reported via the nsIDOMFontFace API. 1.729 + nsAutoString originalFullName; 1.730 + 1.731 + // Call the OTS sanitizer; this will also decode WOFF to sfnt 1.732 + // if necessary. The original data in aFontData is left unchanged. 1.733 + uint32_t saneLen; 1.734 + const uint8_t* saneData = 1.735 + SanitizeOpenTypeData(aFamily, aProxy, aFontData, aLength, saneLen, 1.736 + fontType == GFX_USERFONT_WOFF); 1.737 + if (!saneData) { 1.738 + LogMessage(aFamily, aProxy, "rejected by sanitizer"); 1.739 + } 1.740 + if (saneData) { 1.741 + // The sanitizer ensures that we have a valid sfnt and a usable 1.742 + // name table, so this should never fail unless we're out of 1.743 + // memory, and GetFullNameFromSFNT is not directly exposed to 1.744 + // arbitrary/malicious data from the web. 1.745 + gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen, 1.746 + originalFullName); 1.747 + // Here ownership of saneData is passed to the platform, 1.748 + // which will delete it when no longer required 1.749 + fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy, 1.750 + saneData, 1.751 + saneLen); 1.752 + if (!fe) { 1.753 + LogMessage(aFamily, aProxy, "not usable by platform"); 1.754 + } 1.755 + } 1.756 + 1.757 + if (fe) { 1.758 + // Save a copy of the metadata block (if present) for nsIDOMFontFace 1.759 + // to use if required. Ownership of the metadata block will be passed 1.760 + // to the gfxUserFontData record below. 1.761 + FallibleTArray<uint8_t> metadata; 1.762 + uint32_t metaOrigLen = 0; 1.763 + if (fontType == GFX_USERFONT_WOFF) { 1.764 + CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen); 1.765 + } 1.766 + 1.767 + // copy OpenType feature/language settings from the proxy to the 1.768 + // newly-created font entry 1.769 + fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings); 1.770 + fe->mLanguageOverride = aProxy->mLanguageOverride; 1.771 + StoreUserFontData(fe, aProxy, GetPrivateBrowsing(), 1.772 + originalFullName, &metadata, metaOrigLen); 1.773 +#ifdef PR_LOGGING 1.774 + if (LOG_ENABLED()) { 1.775 + nsAutoCString fontURI; 1.776 + aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI); 1.777 + LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n", 1.778 + this, aProxy->mSrcIndex, fontURI.get(), 1.779 + NS_ConvertUTF16toUTF8(aFamily->Name()).get(), 1.780 + uint32_t(mGeneration))); 1.781 + } 1.782 +#endif 1.783 + ReplaceFontEntry(aFamily, aProxy, fe); 1.784 + UserFontCache::CacheFont(fe); 1.785 + } else { 1.786 +#ifdef PR_LOGGING 1.787 + if (LOG_ENABLED()) { 1.788 + nsAutoCString fontURI; 1.789 + aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI); 1.790 + LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)" 1.791 + " error making platform font\n", 1.792 + this, aProxy->mSrcIndex, fontURI.get(), 1.793 + NS_ConvertUTF16toUTF8(aFamily->Name()).get())); 1.794 + } 1.795 +#endif 1.796 + } 1.797 + 1.798 + // The downloaded data can now be discarded; the font entry is using the 1.799 + // sanitized copy 1.800 + NS_Free((void*)aFontData); 1.801 + 1.802 + return fe; 1.803 +} 1.804 + 1.805 +gfxFontFamily* 1.806 +gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const 1.807 +{ 1.808 + nsAutoString key(aFamilyName); 1.809 + ToLowerCase(key); 1.810 + 1.811 + return mFontFamilies.GetWeak(key); 1.812 +} 1.813 + 1.814 +struct FindFamilyCallbackData { 1.815 + gfxFontEntry *mFontEntry; 1.816 + gfxFontFamily *mFamily; 1.817 +}; 1.818 + 1.819 +static PLDHashOperator 1.820 +FindFamilyCallback(const nsAString& aName, 1.821 + gfxMixedFontFamily* aFamily, 1.822 + void* aUserArg) 1.823 +{ 1.824 + FindFamilyCallbackData *d = static_cast<FindFamilyCallbackData*>(aUserArg); 1.825 + if (aFamily->ContainsFace(d->mFontEntry)) { 1.826 + d->mFamily = aFamily; 1.827 + return PL_DHASH_STOP; 1.828 + } 1.829 + 1.830 + return PL_DHASH_NEXT; 1.831 +} 1.832 + 1.833 +gfxFontFamily* 1.834 +gfxUserFontSet::FindFamilyFor(gfxFontEntry* aFontEntry) const 1.835 +{ 1.836 + FindFamilyCallbackData d = { aFontEntry, nullptr }; 1.837 + mFontFamilies.EnumerateRead(FindFamilyCallback, &d); 1.838 + return d.mFamily; 1.839 +} 1.840 + 1.841 +/////////////////////////////////////////////////////////////////////////////// 1.842 +// gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts 1.843 +// across pages/fontsets rather than instantiating new platform fonts. 1.844 +// 1.845 +// Entries are added to this cache when a platform font is instantiated from 1.846 +// downloaded data, and removed when the platform font entry is destroyed. 1.847 +// We don't need to use a timed expiration scheme here because the gfxFontEntry 1.848 +// for a downloaded font will be kept alive by its corresponding gfxFont 1.849 +// instance(s) until they are deleted, and *that* happens using an expiration 1.850 +// tracker (gfxFontCache). The result is that the downloaded font instances 1.851 +// recorded here will persist between pages and can get reused (provided the 1.852 +// source URI and principal match, of course). 1.853 +/////////////////////////////////////////////////////////////////////////////// 1.854 + 1.855 +nsTHashtable<gfxUserFontSet::UserFontCache::Entry>* 1.856 + gfxUserFontSet::UserFontCache::sUserFonts = nullptr; 1.857 + 1.858 +NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver) 1.859 + 1.860 +PLDHashOperator 1.861 +gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry* aEntry, 1.862 + void* aUserData) 1.863 +{ 1.864 + return aEntry->mPrivate ? PL_DHASH_REMOVE : PL_DHASH_NEXT; 1.865 +} 1.866 + 1.867 +PLDHashOperator 1.868 +gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches(Entry* aEntry, 1.869 + void* aUserData) 1.870 +{ 1.871 + return aEntry->GetFontEntry() == static_cast<gfxFontEntry*>(aUserData) ? 1.872 + PL_DHASH_REMOVE : PL_DHASH_NEXT; 1.873 +} 1.874 + 1.875 +PLDHashOperator 1.876 +gfxUserFontSet::UserFontCache::Entry::DisconnectSVG(Entry* aEntry, 1.877 + void* aUserData) 1.878 +{ 1.879 + aEntry->GetFontEntry()->DisconnectSVG(); 1.880 + return PL_DHASH_NEXT; 1.881 +} 1.882 + 1.883 +NS_IMETHODIMP 1.884 +gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject, 1.885 + const char* aTopic, 1.886 + const char16_t* aData) 1.887 +{ 1.888 + if (!sUserFonts) { 1.889 + return NS_OK; 1.890 + } 1.891 + 1.892 + if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID)) { 1.893 + sUserFonts->Clear(); 1.894 + } else if (!strcmp(aTopic, "last-pb-context-exited")) { 1.895 + sUserFonts->EnumerateEntries(Entry::RemoveIfPrivate, nullptr); 1.896 + } else if (!strcmp(aTopic, "xpcom-shutdown")) { 1.897 + sUserFonts->EnumerateEntries(Entry::DisconnectSVG, nullptr); 1.898 + } else { 1.899 + NS_NOTREACHED("unexpected topic"); 1.900 + } 1.901 + 1.902 + return NS_OK; 1.903 +} 1.904 + 1.905 +bool 1.906 +gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const 1.907 +{ 1.908 + bool equal; 1.909 + if (NS_FAILED(mURI->Equals(aKey->mURI, &equal)) || !equal) { 1.910 + return false; 1.911 + } 1.912 + 1.913 + if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &equal)) || !equal) { 1.914 + return false; 1.915 + } 1.916 + 1.917 + if (mPrivate != aKey->mPrivate) { 1.918 + return false; 1.919 + } 1.920 + 1.921 + const gfxFontEntry *fe = aKey->mFontEntry; 1.922 + if (mFontEntry->mItalic != fe->mItalic || 1.923 + mFontEntry->mWeight != fe->mWeight || 1.924 + mFontEntry->mStretch != fe->mStretch || 1.925 + mFontEntry->mFeatureSettings != fe->mFeatureSettings || 1.926 + mFontEntry->mLanguageOverride != fe->mLanguageOverride || 1.927 + mFontEntry->mFamilyName != fe->mFamilyName) { 1.928 + return false; 1.929 + } 1.930 + 1.931 + return true; 1.932 +} 1.933 + 1.934 +void 1.935 +gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry) 1.936 +{ 1.937 + NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0, 1.938 + "caching a font associated with no family yet"); 1.939 + if (!sUserFonts) { 1.940 + sUserFonts = new nsTHashtable<Entry>; 1.941 + 1.942 + nsCOMPtr<nsIObserverService> obs = 1.943 + mozilla::services::GetObserverService(); 1.944 + if (obs) { 1.945 + Flusher *flusher = new Flusher; 1.946 + obs->AddObserver(flusher, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, 1.947 + false); 1.948 + obs->AddObserver(flusher, "last-pb-context-exited", false); 1.949 + obs->AddObserver(flusher, "xpcom-shutdown", false); 1.950 + } 1.951 + } 1.952 + 1.953 + gfxUserFontData *data = aFontEntry->mUserFontData; 1.954 + sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry, 1.955 + data->mPrivate)); 1.956 + 1.957 +#ifdef DEBUG_USERFONT_CACHE 1.958 + printf("userfontcache added fontentry: %p\n", aFontEntry); 1.959 + Dump(); 1.960 +#endif 1.961 +} 1.962 + 1.963 +void 1.964 +gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry) 1.965 +{ 1.966 + if (!sUserFonts) { 1.967 + // if we've already deleted the cache (i.e. during shutdown), 1.968 + // just ignore this 1.969 + return; 1.970 + } 1.971 + 1.972 + // We can't simply use RemoveEntry here because it's possible the principal 1.973 + // may have changed since the font was cached, in which case the lookup 1.974 + // would no longer find the entry (bug 838105). 1.975 + sUserFonts->EnumerateEntries( 1.976 + gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches, aFontEntry); 1.977 + 1.978 +#ifdef DEBUG_USERFONT_CACHE 1.979 + printf("userfontcache removed fontentry: %p\n", aFontEntry); 1.980 + Dump(); 1.981 +#endif 1.982 +} 1.983 + 1.984 +gfxFontEntry* 1.985 +gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI, 1.986 + nsIPrincipal *aPrincipal, 1.987 + gfxProxyFontEntry *aProxy, 1.988 + bool aPrivate) 1.989 +{ 1.990 + if (!sUserFonts) { 1.991 + return nullptr; 1.992 + } 1.993 + 1.994 + Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy, 1.995 + aPrivate)); 1.996 + if (entry) { 1.997 + return entry->GetFontEntry(); 1.998 + } 1.999 + 1.1000 + return nullptr; 1.1001 +} 1.1002 + 1.1003 +void 1.1004 +gfxUserFontSet::UserFontCache::Shutdown() 1.1005 +{ 1.1006 + if (sUserFonts) { 1.1007 + delete sUserFonts; 1.1008 + sUserFonts = nullptr; 1.1009 + } 1.1010 +} 1.1011 + 1.1012 +#ifdef DEBUG_USERFONT_CACHE 1.1013 + 1.1014 +PLDHashOperator 1.1015 +gfxUserFontSet::UserFontCache::Entry::DumpEntry(Entry* aEntry, void* aUserData) 1.1016 +{ 1.1017 + nsresult rv; 1.1018 + 1.1019 + nsAutoCString principalURISpec; 1.1020 + 1.1021 + nsCOMPtr<nsIURI> principalURI; 1.1022 + rv = aEntry->mPrincipal->GetURI(getter_AddRefs(principalURI)); 1.1023 + if (NS_SUCCEEDED(rv)) { 1.1024 + principalURI->GetSpec(principalURISpec); 1.1025 + } 1.1026 + 1.1027 + bool setDomain = false; 1.1028 + nsCOMPtr<nsIURI> domainURI; 1.1029 + 1.1030 + aEntry->mPrincipal->GetDomain(getter_AddRefs(domainURI)); 1.1031 + if (domainURI) { 1.1032 + setDomain = true; 1.1033 + } 1.1034 + 1.1035 + NS_ASSERTION(aEntry->mURI, "null URI in userfont cache entry"); 1.1036 + 1.1037 + printf("userfontcache fontEntry: %p fonturihash: %8.8x family: %s domainset: %s principal: [%s]\n", 1.1038 + aEntry->mFontEntry, 1.1039 + nsURIHashKey::HashKey(aEntry->mURI), 1.1040 + NS_ConvertUTF16toUTF8(aEntry->mFontEntry->FamilyName()).get(), 1.1041 + (setDomain ? "true" : "false"), 1.1042 + principalURISpec.get() 1.1043 + ); 1.1044 + return PL_DHASH_NEXT; 1.1045 +} 1.1046 + 1.1047 +void 1.1048 +gfxUserFontSet::UserFontCache::Dump() 1.1049 +{ 1.1050 + if (!sUserFonts) { 1.1051 + return; 1.1052 + } 1.1053 + 1.1054 + printf("userfontcache dump count: %d ========\n", sUserFonts->Count()); 1.1055 + sUserFonts->EnumerateEntries(Entry::DumpEntry, nullptr); 1.1056 + printf("userfontcache dump ==================\n"); 1.1057 +} 1.1058 + 1.1059 +#endif