|
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 #ifdef MOZ_LOGGING |
|
7 #define FORCE_PR_LOG /* Allow logging in the release build */ |
|
8 #endif /* MOZ_LOGGING */ |
|
9 #include "prlog.h" |
|
10 |
|
11 #include "gfxUserFontSet.h" |
|
12 #include "nsFont.h" |
|
13 #include "gfxPlatform.h" |
|
14 #include "nsUnicharUtils.h" |
|
15 #include "nsNetUtil.h" |
|
16 #include "nsICacheService.h" |
|
17 #include "nsIProtocolHandler.h" |
|
18 #include "nsIPrincipal.h" |
|
19 #include "gfxFontConstants.h" |
|
20 #include "mozilla/Services.h" |
|
21 #include "mozilla/gfx/2D.h" |
|
22 #include "gfxPlatformFontList.h" |
|
23 |
|
24 #include "opentype-sanitiser.h" |
|
25 #include "ots-memory-stream.h" |
|
26 |
|
27 using namespace mozilla; |
|
28 |
|
29 #ifdef PR_LOGGING |
|
30 PRLogModuleInfo * |
|
31 gfxUserFontSet::GetUserFontsLog() |
|
32 { |
|
33 static PRLogModuleInfo *sLog; |
|
34 if (!sLog) |
|
35 sLog = PR_NewLogModule("userfonts"); |
|
36 return sLog; |
|
37 } |
|
38 #endif /* PR_LOGGING */ |
|
39 |
|
40 #define LOG(args) PR_LOG(GetUserFontsLog(), PR_LOG_DEBUG, args) |
|
41 #define LOG_ENABLED() PR_LOG_TEST(GetUserFontsLog(), PR_LOG_DEBUG) |
|
42 |
|
43 static uint64_t sFontSetGeneration = 0; |
|
44 |
|
45 // TODO: support for unicode ranges not yet implemented |
|
46 |
|
47 gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, |
|
48 uint32_t aWeight, |
|
49 int32_t aStretch, |
|
50 uint32_t aItalicStyle, |
|
51 const nsTArray<gfxFontFeature>& aFeatureSettings, |
|
52 uint32_t aLanguageOverride, |
|
53 gfxSparseBitSet *aUnicodeRanges) |
|
54 : gfxFontEntry(NS_LITERAL_STRING("Proxy")), |
|
55 mLoadingState(NOT_LOADING), |
|
56 mUnsupportedFormat(false), |
|
57 mLoader(nullptr) |
|
58 { |
|
59 mIsProxy = true; |
|
60 mSrcList = aFontFaceSrcList; |
|
61 mSrcIndex = 0; |
|
62 mWeight = aWeight; |
|
63 mStretch = aStretch; |
|
64 // XXX Currently, we don't distinguish 'italic' and 'oblique' styles; |
|
65 // we need to fix this. (Bug 543715) |
|
66 mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; |
|
67 mFeatureSettings.AppendElements(aFeatureSettings); |
|
68 mLanguageOverride = aLanguageOverride; |
|
69 mIsUserFont = true; |
|
70 } |
|
71 |
|
72 gfxProxyFontEntry::~gfxProxyFontEntry() |
|
73 { |
|
74 } |
|
75 |
|
76 bool |
|
77 gfxProxyFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, |
|
78 uint32_t aWeight, |
|
79 int32_t aStretch, |
|
80 uint32_t aItalicStyle, |
|
81 const nsTArray<gfxFontFeature>& aFeatureSettings, |
|
82 uint32_t aLanguageOverride, |
|
83 gfxSparseBitSet *aUnicodeRanges) |
|
84 { |
|
85 // XXX font entries don't distinguish italic from oblique (bug 543715) |
|
86 bool isItalic = |
|
87 (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; |
|
88 |
|
89 return mWeight == aWeight && |
|
90 mStretch == aStretch && |
|
91 mItalic == isItalic && |
|
92 mFeatureSettings == aFeatureSettings && |
|
93 mLanguageOverride == aLanguageOverride && |
|
94 mSrcList == aFontFaceSrcList; |
|
95 // XXX once we support unicode-range (bug 475891), |
|
96 // we'll need to compare that here as well |
|
97 } |
|
98 |
|
99 gfxFont* |
|
100 gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) |
|
101 { |
|
102 // cannot create an actual font for a proxy entry |
|
103 return nullptr; |
|
104 } |
|
105 |
|
106 gfxUserFontSet::gfxUserFontSet() |
|
107 : mFontFamilies(5), mLocalRulesUsed(false) |
|
108 { |
|
109 IncrementGeneration(); |
|
110 gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); |
|
111 if (fp) { |
|
112 fp->AddUserFontSet(this); |
|
113 } |
|
114 } |
|
115 |
|
116 gfxUserFontSet::~gfxUserFontSet() |
|
117 { |
|
118 gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList(); |
|
119 if (fp) { |
|
120 fp->RemoveUserFontSet(this); |
|
121 } |
|
122 } |
|
123 |
|
124 gfxFontEntry* |
|
125 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, |
|
126 const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList, |
|
127 uint32_t aWeight, |
|
128 int32_t aStretch, |
|
129 uint32_t aItalicStyle, |
|
130 const nsTArray<gfxFontFeature>& aFeatureSettings, |
|
131 const nsString& aLanguageOverride, |
|
132 gfxSparseBitSet *aUnicodeRanges) |
|
133 { |
|
134 nsAutoString key(aFamilyName); |
|
135 ToLowerCase(key); |
|
136 |
|
137 bool found; |
|
138 |
|
139 if (aWeight == 0) |
|
140 aWeight = NS_FONT_WEIGHT_NORMAL; |
|
141 |
|
142 // stretch, italic/oblique ==> zero implies normal |
|
143 |
|
144 gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found); |
|
145 if (!family) { |
|
146 family = new gfxMixedFontFamily(aFamilyName); |
|
147 mFontFamilies.Put(key, family); |
|
148 } |
|
149 |
|
150 uint32_t languageOverride = |
|
151 gfxFontStyle::ParseFontLanguageOverride(aLanguageOverride); |
|
152 |
|
153 // If there's already a proxy in the family whose descriptors all match, |
|
154 // we can just move it to the end of the list instead of adding a new |
|
155 // face that will always "shadow" the old one. |
|
156 // Note that we can't do this for "real" (non-proxy) entries, even if the |
|
157 // style descriptors match, as they might have had a different source list, |
|
158 // but we no longer have the old source list available to check. |
|
159 nsTArray<nsRefPtr<gfxFontEntry> >& fontList = family->GetFontList(); |
|
160 for (uint32_t i = 0, count = fontList.Length(); i < count; i++) { |
|
161 if (!fontList[i]->mIsProxy) { |
|
162 continue; |
|
163 } |
|
164 |
|
165 gfxProxyFontEntry *existingProxyEntry = |
|
166 static_cast<gfxProxyFontEntry*>(fontList[i].get()); |
|
167 if (!existingProxyEntry->Matches(aFontFaceSrcList, |
|
168 aWeight, aStretch, aItalicStyle, |
|
169 aFeatureSettings, languageOverride, |
|
170 aUnicodeRanges)) { |
|
171 continue; |
|
172 } |
|
173 |
|
174 // We've found an entry that matches the new face exactly, so just add |
|
175 // it to the end of the list. gfxMixedFontFamily::AddFontEntry() will |
|
176 // automatically remove any earlier occurrence of the same proxy. |
|
177 family->AddFontEntry(existingProxyEntry); |
|
178 return existingProxyEntry; |
|
179 } |
|
180 |
|
181 // construct a new face and add it into the family |
|
182 gfxProxyFontEntry *proxyEntry = |
|
183 new gfxProxyFontEntry(aFontFaceSrcList, aWeight, aStretch, |
|
184 aItalicStyle, |
|
185 aFeatureSettings, |
|
186 languageOverride, |
|
187 aUnicodeRanges); |
|
188 family->AddFontEntry(proxyEntry); |
|
189 #ifdef PR_LOGGING |
|
190 if (LOG_ENABLED()) { |
|
191 LOG(("userfonts (%p) added (%s) with style: %s weight: %d stretch: %d", |
|
192 this, NS_ConvertUTF16toUTF8(aFamilyName).get(), |
|
193 (aItalicStyle & NS_FONT_STYLE_ITALIC ? "italic" : |
|
194 (aItalicStyle & NS_FONT_STYLE_OBLIQUE ? "oblique" : "normal")), |
|
195 aWeight, aStretch)); |
|
196 } |
|
197 #endif |
|
198 |
|
199 return proxyEntry; |
|
200 } |
|
201 |
|
202 void |
|
203 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName, |
|
204 gfxFontEntry *aFontEntry) |
|
205 { |
|
206 nsAutoString key(aFamilyName); |
|
207 ToLowerCase(key); |
|
208 |
|
209 bool found; |
|
210 |
|
211 gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found); |
|
212 if (!family) { |
|
213 family = new gfxMixedFontFamily(aFamilyName); |
|
214 mFontFamilies.Put(key, family); |
|
215 } |
|
216 |
|
217 family->AddFontEntry(aFontEntry); |
|
218 } |
|
219 |
|
220 gfxFontEntry* |
|
221 gfxUserFontSet::FindFontEntry(gfxFontFamily *aFamily, |
|
222 const gfxFontStyle& aFontStyle, |
|
223 bool& aNeedsBold, |
|
224 bool& aWaitForUserFont) |
|
225 { |
|
226 aWaitForUserFont = false; |
|
227 gfxMixedFontFamily *family = static_cast<gfxMixedFontFamily*>(aFamily); |
|
228 |
|
229 gfxFontEntry* fe = family->FindFontForStyle(aFontStyle, aNeedsBold); |
|
230 |
|
231 // if not a proxy, font has already been loaded |
|
232 if (!fe->mIsProxy) { |
|
233 return fe; |
|
234 } |
|
235 |
|
236 gfxProxyFontEntry *proxyEntry = static_cast<gfxProxyFontEntry*> (fe); |
|
237 |
|
238 // if currently loading, return null for now |
|
239 if (proxyEntry->mLoadingState > gfxProxyFontEntry::NOT_LOADING) { |
|
240 aWaitForUserFont = |
|
241 (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY); |
|
242 return nullptr; |
|
243 } |
|
244 |
|
245 // hasn't been loaded yet, start the load process |
|
246 LoadStatus status; |
|
247 |
|
248 // NOTE that if all sources in the entry fail, this will delete proxyEntry, |
|
249 // so we cannot use it again if status==STATUS_END_OF_LIST |
|
250 status = LoadNext(family, proxyEntry); |
|
251 |
|
252 // if the load succeeded immediately, the font entry was replaced so |
|
253 // search again |
|
254 if (status == STATUS_LOADED) { |
|
255 return family->FindFontForStyle(aFontStyle, aNeedsBold); |
|
256 } |
|
257 |
|
258 // check whether we should wait for load to complete before painting |
|
259 // a fallback font -- but not if all sources failed (bug 633500) |
|
260 aWaitForUserFont = (status != STATUS_END_OF_LIST) && |
|
261 (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY); |
|
262 |
|
263 // if either loading or an error occurred, return null |
|
264 return nullptr; |
|
265 } |
|
266 |
|
267 // Based on ots::ExpandingMemoryStream from ots-memory-stream.h, |
|
268 // adapted to use Mozilla allocators and to allow the final |
|
269 // memory buffer to be adopted by the client. |
|
270 class ExpandingMemoryStream : public ots::OTSStream { |
|
271 public: |
|
272 ExpandingMemoryStream(size_t initial, size_t limit) |
|
273 : mLength(initial), mLimit(limit), mOff(0) { |
|
274 mPtr = NS_Alloc(mLength); |
|
275 } |
|
276 |
|
277 ~ExpandingMemoryStream() { |
|
278 NS_Free(mPtr); |
|
279 } |
|
280 |
|
281 // return the buffer, and give up ownership of it |
|
282 // so the caller becomes responsible to call NS_Free |
|
283 // when finished with it |
|
284 void* forget() { |
|
285 void* p = mPtr; |
|
286 mPtr = nullptr; |
|
287 return p; |
|
288 } |
|
289 |
|
290 bool WriteRaw(const void *data, size_t length) { |
|
291 if ((mOff + length > mLength) || |
|
292 (mLength > std::numeric_limits<size_t>::max() - mOff)) { |
|
293 if (mLength == mLimit) { |
|
294 return false; |
|
295 } |
|
296 size_t newLength = (mLength + 1) * 2; |
|
297 if (newLength < mLength) { |
|
298 return false; |
|
299 } |
|
300 if (newLength > mLimit) { |
|
301 newLength = mLimit; |
|
302 } |
|
303 mPtr = NS_Realloc(mPtr, newLength); |
|
304 mLength = newLength; |
|
305 return WriteRaw(data, length); |
|
306 } |
|
307 std::memcpy(static_cast<char*>(mPtr) + mOff, data, length); |
|
308 mOff += length; |
|
309 return true; |
|
310 } |
|
311 |
|
312 bool Seek(off_t position) { |
|
313 if (position < 0) { |
|
314 return false; |
|
315 } |
|
316 if (static_cast<size_t>(position) > mLength) { |
|
317 return false; |
|
318 } |
|
319 mOff = position; |
|
320 return true; |
|
321 } |
|
322 |
|
323 off_t Tell() const { |
|
324 return mOff; |
|
325 } |
|
326 |
|
327 private: |
|
328 void* mPtr; |
|
329 size_t mLength; |
|
330 const size_t mLimit; |
|
331 off_t mOff; |
|
332 }; |
|
333 |
|
334 static ots::TableAction |
|
335 OTSTableAction(uint32_t aTag, void *aUserData) |
|
336 { |
|
337 // preserve Graphite and SVG tables |
|
338 if (aTag == TRUETYPE_TAG('S', 'i', 'l', 'f') || |
|
339 aTag == TRUETYPE_TAG('S', 'i', 'l', 'l') || |
|
340 aTag == TRUETYPE_TAG('G', 'l', 'o', 'c') || |
|
341 aTag == TRUETYPE_TAG('G', 'l', 'a', 't') || |
|
342 aTag == TRUETYPE_TAG('F', 'e', 'a', 't') || |
|
343 aTag == TRUETYPE_TAG('S', 'V', 'G', ' ')) { |
|
344 return ots::TABLE_ACTION_PASSTHRU; |
|
345 } |
|
346 return ots::TABLE_ACTION_DEFAULT; |
|
347 } |
|
348 |
|
349 struct OTSCallbackUserData { |
|
350 gfxUserFontSet *mFontSet; |
|
351 gfxMixedFontFamily *mFamily; |
|
352 gfxProxyFontEntry *mProxy; |
|
353 }; |
|
354 |
|
355 /* static */ bool |
|
356 gfxUserFontSet::OTSMessage(void *aUserData, const char *format, ...) |
|
357 { |
|
358 va_list va; |
|
359 va_start(va, format); |
|
360 |
|
361 // buf should be more than adequate for any message OTS generates, |
|
362 // so we don't worry about checking the result of vsnprintf() |
|
363 char buf[512]; |
|
364 (void)vsnprintf(buf, sizeof(buf), format, va); |
|
365 |
|
366 va_end(va); |
|
367 |
|
368 OTSCallbackUserData *d = static_cast<OTSCallbackUserData*>(aUserData); |
|
369 d->mFontSet->LogMessage(d->mFamily, d->mProxy, buf); |
|
370 |
|
371 return false; |
|
372 } |
|
373 |
|
374 // Call the OTS library to sanitize an sfnt before attempting to use it. |
|
375 // Returns a newly-allocated block, or nullptr in case of fatal errors. |
|
376 const uint8_t* |
|
377 gfxUserFontSet::SanitizeOpenTypeData(gfxMixedFontFamily *aFamily, |
|
378 gfxProxyFontEntry *aProxy, |
|
379 const uint8_t* aData, uint32_t aLength, |
|
380 uint32_t& aSaneLength, bool aIsCompressed) |
|
381 { |
|
382 // limit output/expansion to 256MB |
|
383 ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength, |
|
384 1024 * 1024 * 256); |
|
385 |
|
386 OTSCallbackUserData userData; |
|
387 userData.mFontSet = this; |
|
388 userData.mFamily = aFamily; |
|
389 userData.mProxy = aProxy; |
|
390 |
|
391 ots::SetTableActionCallback(&OTSTableAction, nullptr); |
|
392 ots::SetMessageCallback(&gfxUserFontSet::OTSMessage, &userData); |
|
393 |
|
394 if (ots::Process(&output, aData, aLength)) { |
|
395 aSaneLength = output.Tell(); |
|
396 return static_cast<uint8_t*>(output.forget()); |
|
397 } else { |
|
398 aSaneLength = 0; |
|
399 return nullptr; |
|
400 } |
|
401 } |
|
402 |
|
403 static void |
|
404 StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy, |
|
405 bool aPrivate, const nsAString& aOriginalName, |
|
406 FallibleTArray<uint8_t>* aMetadata, uint32_t aMetaOrigLen) |
|
407 { |
|
408 if (!aFontEntry->mUserFontData) { |
|
409 aFontEntry->mUserFontData = new gfxUserFontData; |
|
410 } |
|
411 gfxUserFontData* userFontData = aFontEntry->mUserFontData; |
|
412 userFontData->mSrcIndex = aProxy->mSrcIndex; |
|
413 const gfxFontFaceSrc& src = aProxy->mSrcList[aProxy->mSrcIndex]; |
|
414 if (src.mIsLocal) { |
|
415 userFontData->mLocalName = src.mLocalName; |
|
416 } else { |
|
417 userFontData->mURI = src.mURI; |
|
418 userFontData->mPrincipal = aProxy->mPrincipal; |
|
419 } |
|
420 userFontData->mPrivate = aPrivate; |
|
421 userFontData->mFormat = src.mFormatFlags; |
|
422 userFontData->mRealName = aOriginalName; |
|
423 if (aMetadata) { |
|
424 userFontData->mMetadata.SwapElements(*aMetadata); |
|
425 userFontData->mMetaOrigLen = aMetaOrigLen; |
|
426 } |
|
427 } |
|
428 |
|
429 struct WOFFHeader { |
|
430 AutoSwap_PRUint32 signature; |
|
431 AutoSwap_PRUint32 flavor; |
|
432 AutoSwap_PRUint32 length; |
|
433 AutoSwap_PRUint16 numTables; |
|
434 AutoSwap_PRUint16 reserved; |
|
435 AutoSwap_PRUint32 totalSfntSize; |
|
436 AutoSwap_PRUint16 majorVersion; |
|
437 AutoSwap_PRUint16 minorVersion; |
|
438 AutoSwap_PRUint32 metaOffset; |
|
439 AutoSwap_PRUint32 metaCompLen; |
|
440 AutoSwap_PRUint32 metaOrigLen; |
|
441 AutoSwap_PRUint32 privOffset; |
|
442 AutoSwap_PRUint32 privLen; |
|
443 }; |
|
444 |
|
445 void |
|
446 gfxUserFontSet::CopyWOFFMetadata(const uint8_t* aFontData, |
|
447 uint32_t aLength, |
|
448 FallibleTArray<uint8_t>* aMetadata, |
|
449 uint32_t* aMetaOrigLen) |
|
450 { |
|
451 // This function may be called with arbitrary, unvalidated "font" data |
|
452 // from @font-face, so it needs to be careful to bounds-check, etc., |
|
453 // before trying to read anything. |
|
454 // This just saves a copy of the compressed data block; it does NOT check |
|
455 // that the block can be successfully decompressed, or that it contains |
|
456 // well-formed/valid XML metadata. |
|
457 if (aLength < sizeof(WOFFHeader)) { |
|
458 return; |
|
459 } |
|
460 const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData); |
|
461 uint32_t metaOffset = woff->metaOffset; |
|
462 uint32_t metaCompLen = woff->metaCompLen; |
|
463 if (!metaOffset || !metaCompLen || !woff->metaOrigLen) { |
|
464 return; |
|
465 } |
|
466 if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) { |
|
467 return; |
|
468 } |
|
469 if (!aMetadata->SetLength(woff->metaCompLen)) { |
|
470 return; |
|
471 } |
|
472 memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen); |
|
473 *aMetaOrigLen = woff->metaOrigLen; |
|
474 } |
|
475 |
|
476 // This is called when a font download finishes. |
|
477 // Ownership of aFontData passes in here, and the font set must |
|
478 // ensure that it is eventually deleted via NS_Free(). |
|
479 bool |
|
480 gfxUserFontSet::OnLoadComplete(gfxMixedFontFamily *aFamily, |
|
481 gfxProxyFontEntry *aProxy, |
|
482 const uint8_t *aFontData, uint32_t aLength, |
|
483 nsresult aDownloadStatus) |
|
484 { |
|
485 // forget about the loader, as we no longer potentially need to cancel it |
|
486 // if the entry is obsoleted |
|
487 aProxy->mLoader = nullptr; |
|
488 |
|
489 // download successful, make platform font using font data |
|
490 if (NS_SUCCEEDED(aDownloadStatus)) { |
|
491 gfxFontEntry *fe = LoadFont(aFamily, aProxy, aFontData, aLength); |
|
492 aFontData = nullptr; |
|
493 |
|
494 if (fe) { |
|
495 IncrementGeneration(); |
|
496 return true; |
|
497 } |
|
498 |
|
499 } else { |
|
500 // download failed |
|
501 LogMessage(aFamily, aProxy, |
|
502 "download failed", nsIScriptError::errorFlag, |
|
503 aDownloadStatus); |
|
504 } |
|
505 |
|
506 if (aFontData) { |
|
507 NS_Free((void*)aFontData); |
|
508 } |
|
509 |
|
510 // error occurred, load next src |
|
511 (void)LoadNext(aFamily, aProxy); |
|
512 |
|
513 // We ignore the status returned by LoadNext(); |
|
514 // even if loading failed, we need to bump the font-set generation |
|
515 // and return true in order to trigger reflow, so that fallback |
|
516 // will be used where the text was "masked" by the pending download |
|
517 IncrementGeneration(); |
|
518 return true; |
|
519 } |
|
520 |
|
521 |
|
522 gfxUserFontSet::LoadStatus |
|
523 gfxUserFontSet::LoadNext(gfxMixedFontFamily *aFamily, |
|
524 gfxProxyFontEntry *aProxyEntry) |
|
525 { |
|
526 uint32_t numSrc = aProxyEntry->mSrcList.Length(); |
|
527 |
|
528 NS_ASSERTION(aProxyEntry->mSrcIndex < numSrc, |
|
529 "already at the end of the src list for user font"); |
|
530 |
|
531 if (aProxyEntry->mLoadingState == gfxProxyFontEntry::NOT_LOADING) { |
|
532 aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_STARTED; |
|
533 aProxyEntry->mUnsupportedFormat = false; |
|
534 } else { |
|
535 // we were already loading; move to the next source, |
|
536 // but don't reset state - if we've already timed out, |
|
537 // that counts against the new download |
|
538 aProxyEntry->mSrcIndex++; |
|
539 } |
|
540 |
|
541 /* If there are any urls, prefer them to local */ |
|
542 bool listHasURL = false; |
|
543 for (uint32_t i = aProxyEntry->mSrcIndex; i < numSrc; i++) { |
|
544 const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[i]; |
|
545 if (!currSrc.mIsLocal) { |
|
546 listHasURL = true; |
|
547 break; |
|
548 } |
|
549 } |
|
550 nsPresContext *pres = GetPresContext(); |
|
551 /* If we have no pres context, simply fail this load */ |
|
552 if (!pres) listHasURL = true; |
|
553 |
|
554 // load each src entry in turn, until a local face is found |
|
555 // or a download begins successfully |
|
556 while (aProxyEntry->mSrcIndex < numSrc) { |
|
557 const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex]; |
|
558 |
|
559 // src local ==> lookup and load immediately |
|
560 |
|
561 if (!listHasURL && currSrc.mIsLocal) { |
|
562 nsFont font; |
|
563 font.name = currSrc.mLocalName; |
|
564 gfxFontEntry *fe = |
|
565 gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry, |
|
566 currSrc.mLocalName); |
|
567 pres->AddFontAttempt(font); |
|
568 |
|
569 /* No more fonts for you */ |
|
570 if (pres->FontAttemptCountReached(font) || |
|
571 pres->FontUseCountReached(font)) { |
|
572 break; |
|
573 } |
|
574 |
|
575 mLocalRulesUsed = true; |
|
576 if (fe) { |
|
577 pres->AddFontUse(font); |
|
578 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n", |
|
579 this, aProxyEntry->mSrcIndex, |
|
580 NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), |
|
581 NS_ConvertUTF16toUTF8(aFamily->Name()).get(), |
|
582 uint32_t(mGeneration))); |
|
583 fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings); |
|
584 fe->mLanguageOverride = aProxyEntry->mLanguageOverride; |
|
585 // For src:local(), we don't care whether the request is from |
|
586 // a private window as there's no issue of caching resources; |
|
587 // local fonts are just available all the time. |
|
588 StoreUserFontData(fe, aProxyEntry, false, nsString(), nullptr, 0); |
|
589 ReplaceFontEntry(aFamily, aProxyEntry, fe); |
|
590 return STATUS_LOADED; |
|
591 } else { |
|
592 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n", |
|
593 this, aProxyEntry->mSrcIndex, |
|
594 NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(), |
|
595 NS_ConvertUTF16toUTF8(aFamily->Name()).get())); |
|
596 } |
|
597 } |
|
598 |
|
599 // src url ==> start the load process |
|
600 else { |
|
601 if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI, |
|
602 currSrc.mFormatFlags)) { |
|
603 |
|
604 nsIPrincipal *principal = nullptr; |
|
605 bool bypassCache; |
|
606 nsresult rv = CheckFontLoad(&currSrc, &principal, &bypassCache); |
|
607 |
|
608 if (NS_SUCCEEDED(rv) && principal != nullptr) { |
|
609 if (!bypassCache) { |
|
610 // see if we have an existing entry for this source |
|
611 gfxFontEntry *fe = |
|
612 UserFontCache::GetFont(currSrc.mURI, principal, |
|
613 aProxyEntry, |
|
614 GetPrivateBrowsing()); |
|
615 if (fe) { |
|
616 ReplaceFontEntry(aFamily, aProxyEntry, fe); |
|
617 return STATUS_LOADED; |
|
618 } |
|
619 } |
|
620 |
|
621 // record the principal returned by CheckFontLoad, |
|
622 // for use when creating a channel |
|
623 // and when caching the loaded entry |
|
624 aProxyEntry->mPrincipal = principal; |
|
625 |
|
626 bool loadDoesntSpin = false; |
|
627 rv = NS_URIChainHasFlags(currSrc.mURI, |
|
628 nsIProtocolHandler::URI_SYNC_LOAD_IS_OK, |
|
629 &loadDoesntSpin); |
|
630 if (NS_SUCCEEDED(rv) && loadDoesntSpin) { |
|
631 uint8_t *buffer = nullptr; |
|
632 uint32_t bufferLength = 0; |
|
633 |
|
634 // sync load font immediately |
|
635 rv = SyncLoadFontData(aProxyEntry, &currSrc, |
|
636 buffer, bufferLength); |
|
637 if (NS_SUCCEEDED(rv) && LoadFont(aFamily, aProxyEntry, |
|
638 buffer, bufferLength)) { |
|
639 return STATUS_LOADED; |
|
640 } else { |
|
641 LogMessage(aFamily, aProxyEntry, |
|
642 "font load failed", |
|
643 nsIScriptError::errorFlag, rv); |
|
644 } |
|
645 } else { |
|
646 // otherwise load font async |
|
647 rv = StartLoad(aFamily, aProxyEntry, &currSrc); |
|
648 if (NS_SUCCEEDED(rv)) { |
|
649 #ifdef PR_LOGGING |
|
650 if (LOG_ENABLED()) { |
|
651 nsAutoCString fontURI; |
|
652 currSrc.mURI->GetSpec(fontURI); |
|
653 LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n", |
|
654 this, aProxyEntry->mSrcIndex, fontURI.get(), |
|
655 NS_ConvertUTF16toUTF8(aFamily->Name()).get())); |
|
656 } |
|
657 #endif |
|
658 return STATUS_LOADING; |
|
659 } else { |
|
660 LogMessage(aFamily, aProxyEntry, |
|
661 "download failed", |
|
662 nsIScriptError::errorFlag, rv); |
|
663 } |
|
664 } |
|
665 } else { |
|
666 LogMessage(aFamily, aProxyEntry, "download not allowed", |
|
667 nsIScriptError::errorFlag, rv); |
|
668 } |
|
669 } else { |
|
670 // We don't log a warning to the web console yet, |
|
671 // as another source may load successfully |
|
672 aProxyEntry->mUnsupportedFormat = true; |
|
673 } |
|
674 } |
|
675 |
|
676 aProxyEntry->mSrcIndex++; |
|
677 } |
|
678 |
|
679 if (aProxyEntry->mUnsupportedFormat) { |
|
680 LogMessage(aFamily, aProxyEntry, "no supported format found", |
|
681 nsIScriptError::warningFlag); |
|
682 } |
|
683 |
|
684 // all src's failed; mark this entry as unusable (so fallback will occur) |
|
685 LOG(("userfonts (%p) failed all src for (%s)\n", |
|
686 this, NS_ConvertUTF16toUTF8(aFamily->Name()).get())); |
|
687 aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_FAILED; |
|
688 |
|
689 return STATUS_END_OF_LIST; |
|
690 } |
|
691 |
|
692 void |
|
693 gfxUserFontSet::IncrementGeneration() |
|
694 { |
|
695 // add one, increment again if zero |
|
696 ++sFontSetGeneration; |
|
697 if (sFontSetGeneration == 0) |
|
698 ++sFontSetGeneration; |
|
699 mGeneration = sFontSetGeneration; |
|
700 } |
|
701 |
|
702 void |
|
703 gfxUserFontSet::RebuildLocalRules() |
|
704 { |
|
705 if (mLocalRulesUsed) { |
|
706 DoRebuildUserFontSet(); |
|
707 } |
|
708 } |
|
709 |
|
710 gfxFontEntry* |
|
711 gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily, |
|
712 gfxProxyFontEntry *aProxy, |
|
713 const uint8_t *aFontData, uint32_t &aLength) |
|
714 { |
|
715 gfxFontEntry *fe = nullptr; |
|
716 |
|
717 gfxUserFontType fontType = |
|
718 gfxFontUtils::DetermineFontDataType(aFontData, aLength); |
|
719 |
|
720 // Unwrap/decompress/sanitize or otherwise munge the downloaded data |
|
721 // to make a usable sfnt structure. |
|
722 |
|
723 // Because platform font activation code may replace the name table |
|
724 // in the font with a synthetic one, we save the original name so that |
|
725 // it can be reported via the nsIDOMFontFace API. |
|
726 nsAutoString originalFullName; |
|
727 |
|
728 // Call the OTS sanitizer; this will also decode WOFF to sfnt |
|
729 // if necessary. The original data in aFontData is left unchanged. |
|
730 uint32_t saneLen; |
|
731 const uint8_t* saneData = |
|
732 SanitizeOpenTypeData(aFamily, aProxy, aFontData, aLength, saneLen, |
|
733 fontType == GFX_USERFONT_WOFF); |
|
734 if (!saneData) { |
|
735 LogMessage(aFamily, aProxy, "rejected by sanitizer"); |
|
736 } |
|
737 if (saneData) { |
|
738 // The sanitizer ensures that we have a valid sfnt and a usable |
|
739 // name table, so this should never fail unless we're out of |
|
740 // memory, and GetFullNameFromSFNT is not directly exposed to |
|
741 // arbitrary/malicious data from the web. |
|
742 gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen, |
|
743 originalFullName); |
|
744 // Here ownership of saneData is passed to the platform, |
|
745 // which will delete it when no longer required |
|
746 fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy, |
|
747 saneData, |
|
748 saneLen); |
|
749 if (!fe) { |
|
750 LogMessage(aFamily, aProxy, "not usable by platform"); |
|
751 } |
|
752 } |
|
753 |
|
754 if (fe) { |
|
755 // Save a copy of the metadata block (if present) for nsIDOMFontFace |
|
756 // to use if required. Ownership of the metadata block will be passed |
|
757 // to the gfxUserFontData record below. |
|
758 FallibleTArray<uint8_t> metadata; |
|
759 uint32_t metaOrigLen = 0; |
|
760 if (fontType == GFX_USERFONT_WOFF) { |
|
761 CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen); |
|
762 } |
|
763 |
|
764 // copy OpenType feature/language settings from the proxy to the |
|
765 // newly-created font entry |
|
766 fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings); |
|
767 fe->mLanguageOverride = aProxy->mLanguageOverride; |
|
768 StoreUserFontData(fe, aProxy, GetPrivateBrowsing(), |
|
769 originalFullName, &metadata, metaOrigLen); |
|
770 #ifdef PR_LOGGING |
|
771 if (LOG_ENABLED()) { |
|
772 nsAutoCString fontURI; |
|
773 aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI); |
|
774 LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n", |
|
775 this, aProxy->mSrcIndex, fontURI.get(), |
|
776 NS_ConvertUTF16toUTF8(aFamily->Name()).get(), |
|
777 uint32_t(mGeneration))); |
|
778 } |
|
779 #endif |
|
780 ReplaceFontEntry(aFamily, aProxy, fe); |
|
781 UserFontCache::CacheFont(fe); |
|
782 } else { |
|
783 #ifdef PR_LOGGING |
|
784 if (LOG_ENABLED()) { |
|
785 nsAutoCString fontURI; |
|
786 aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI); |
|
787 LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)" |
|
788 " error making platform font\n", |
|
789 this, aProxy->mSrcIndex, fontURI.get(), |
|
790 NS_ConvertUTF16toUTF8(aFamily->Name()).get())); |
|
791 } |
|
792 #endif |
|
793 } |
|
794 |
|
795 // The downloaded data can now be discarded; the font entry is using the |
|
796 // sanitized copy |
|
797 NS_Free((void*)aFontData); |
|
798 |
|
799 return fe; |
|
800 } |
|
801 |
|
802 gfxFontFamily* |
|
803 gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const |
|
804 { |
|
805 nsAutoString key(aFamilyName); |
|
806 ToLowerCase(key); |
|
807 |
|
808 return mFontFamilies.GetWeak(key); |
|
809 } |
|
810 |
|
811 struct FindFamilyCallbackData { |
|
812 gfxFontEntry *mFontEntry; |
|
813 gfxFontFamily *mFamily; |
|
814 }; |
|
815 |
|
816 static PLDHashOperator |
|
817 FindFamilyCallback(const nsAString& aName, |
|
818 gfxMixedFontFamily* aFamily, |
|
819 void* aUserArg) |
|
820 { |
|
821 FindFamilyCallbackData *d = static_cast<FindFamilyCallbackData*>(aUserArg); |
|
822 if (aFamily->ContainsFace(d->mFontEntry)) { |
|
823 d->mFamily = aFamily; |
|
824 return PL_DHASH_STOP; |
|
825 } |
|
826 |
|
827 return PL_DHASH_NEXT; |
|
828 } |
|
829 |
|
830 gfxFontFamily* |
|
831 gfxUserFontSet::FindFamilyFor(gfxFontEntry* aFontEntry) const |
|
832 { |
|
833 FindFamilyCallbackData d = { aFontEntry, nullptr }; |
|
834 mFontFamilies.EnumerateRead(FindFamilyCallback, &d); |
|
835 return d.mFamily; |
|
836 } |
|
837 |
|
838 /////////////////////////////////////////////////////////////////////////////// |
|
839 // gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts |
|
840 // across pages/fontsets rather than instantiating new platform fonts. |
|
841 // |
|
842 // Entries are added to this cache when a platform font is instantiated from |
|
843 // downloaded data, and removed when the platform font entry is destroyed. |
|
844 // We don't need to use a timed expiration scheme here because the gfxFontEntry |
|
845 // for a downloaded font will be kept alive by its corresponding gfxFont |
|
846 // instance(s) until they are deleted, and *that* happens using an expiration |
|
847 // tracker (gfxFontCache). The result is that the downloaded font instances |
|
848 // recorded here will persist between pages and can get reused (provided the |
|
849 // source URI and principal match, of course). |
|
850 /////////////////////////////////////////////////////////////////////////////// |
|
851 |
|
852 nsTHashtable<gfxUserFontSet::UserFontCache::Entry>* |
|
853 gfxUserFontSet::UserFontCache::sUserFonts = nullptr; |
|
854 |
|
855 NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver) |
|
856 |
|
857 PLDHashOperator |
|
858 gfxUserFontSet::UserFontCache::Entry::RemoveIfPrivate(Entry* aEntry, |
|
859 void* aUserData) |
|
860 { |
|
861 return aEntry->mPrivate ? PL_DHASH_REMOVE : PL_DHASH_NEXT; |
|
862 } |
|
863 |
|
864 PLDHashOperator |
|
865 gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches(Entry* aEntry, |
|
866 void* aUserData) |
|
867 { |
|
868 return aEntry->GetFontEntry() == static_cast<gfxFontEntry*>(aUserData) ? |
|
869 PL_DHASH_REMOVE : PL_DHASH_NEXT; |
|
870 } |
|
871 |
|
872 PLDHashOperator |
|
873 gfxUserFontSet::UserFontCache::Entry::DisconnectSVG(Entry* aEntry, |
|
874 void* aUserData) |
|
875 { |
|
876 aEntry->GetFontEntry()->DisconnectSVG(); |
|
877 return PL_DHASH_NEXT; |
|
878 } |
|
879 |
|
880 NS_IMETHODIMP |
|
881 gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject, |
|
882 const char* aTopic, |
|
883 const char16_t* aData) |
|
884 { |
|
885 if (!sUserFonts) { |
|
886 return NS_OK; |
|
887 } |
|
888 |
|
889 if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID)) { |
|
890 sUserFonts->Clear(); |
|
891 } else if (!strcmp(aTopic, "last-pb-context-exited")) { |
|
892 sUserFonts->EnumerateEntries(Entry::RemoveIfPrivate, nullptr); |
|
893 } else if (!strcmp(aTopic, "xpcom-shutdown")) { |
|
894 sUserFonts->EnumerateEntries(Entry::DisconnectSVG, nullptr); |
|
895 } else { |
|
896 NS_NOTREACHED("unexpected topic"); |
|
897 } |
|
898 |
|
899 return NS_OK; |
|
900 } |
|
901 |
|
902 bool |
|
903 gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const |
|
904 { |
|
905 bool equal; |
|
906 if (NS_FAILED(mURI->Equals(aKey->mURI, &equal)) || !equal) { |
|
907 return false; |
|
908 } |
|
909 |
|
910 if (NS_FAILED(mPrincipal->Equals(aKey->mPrincipal, &equal)) || !equal) { |
|
911 return false; |
|
912 } |
|
913 |
|
914 if (mPrivate != aKey->mPrivate) { |
|
915 return false; |
|
916 } |
|
917 |
|
918 const gfxFontEntry *fe = aKey->mFontEntry; |
|
919 if (mFontEntry->mItalic != fe->mItalic || |
|
920 mFontEntry->mWeight != fe->mWeight || |
|
921 mFontEntry->mStretch != fe->mStretch || |
|
922 mFontEntry->mFeatureSettings != fe->mFeatureSettings || |
|
923 mFontEntry->mLanguageOverride != fe->mLanguageOverride || |
|
924 mFontEntry->mFamilyName != fe->mFamilyName) { |
|
925 return false; |
|
926 } |
|
927 |
|
928 return true; |
|
929 } |
|
930 |
|
931 void |
|
932 gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry *aFontEntry) |
|
933 { |
|
934 NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0, |
|
935 "caching a font associated with no family yet"); |
|
936 if (!sUserFonts) { |
|
937 sUserFonts = new nsTHashtable<Entry>; |
|
938 |
|
939 nsCOMPtr<nsIObserverService> obs = |
|
940 mozilla::services::GetObserverService(); |
|
941 if (obs) { |
|
942 Flusher *flusher = new Flusher; |
|
943 obs->AddObserver(flusher, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, |
|
944 false); |
|
945 obs->AddObserver(flusher, "last-pb-context-exited", false); |
|
946 obs->AddObserver(flusher, "xpcom-shutdown", false); |
|
947 } |
|
948 } |
|
949 |
|
950 gfxUserFontData *data = aFontEntry->mUserFontData; |
|
951 sUserFonts->PutEntry(Key(data->mURI, data->mPrincipal, aFontEntry, |
|
952 data->mPrivate)); |
|
953 |
|
954 #ifdef DEBUG_USERFONT_CACHE |
|
955 printf("userfontcache added fontentry: %p\n", aFontEntry); |
|
956 Dump(); |
|
957 #endif |
|
958 } |
|
959 |
|
960 void |
|
961 gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry *aFontEntry) |
|
962 { |
|
963 if (!sUserFonts) { |
|
964 // if we've already deleted the cache (i.e. during shutdown), |
|
965 // just ignore this |
|
966 return; |
|
967 } |
|
968 |
|
969 // We can't simply use RemoveEntry here because it's possible the principal |
|
970 // may have changed since the font was cached, in which case the lookup |
|
971 // would no longer find the entry (bug 838105). |
|
972 sUserFonts->EnumerateEntries( |
|
973 gfxUserFontSet::UserFontCache::Entry::RemoveIfMatches, aFontEntry); |
|
974 |
|
975 #ifdef DEBUG_USERFONT_CACHE |
|
976 printf("userfontcache removed fontentry: %p\n", aFontEntry); |
|
977 Dump(); |
|
978 #endif |
|
979 } |
|
980 |
|
981 gfxFontEntry* |
|
982 gfxUserFontSet::UserFontCache::GetFont(nsIURI *aSrcURI, |
|
983 nsIPrincipal *aPrincipal, |
|
984 gfxProxyFontEntry *aProxy, |
|
985 bool aPrivate) |
|
986 { |
|
987 if (!sUserFonts) { |
|
988 return nullptr; |
|
989 } |
|
990 |
|
991 Entry* entry = sUserFonts->GetEntry(Key(aSrcURI, aPrincipal, aProxy, |
|
992 aPrivate)); |
|
993 if (entry) { |
|
994 return entry->GetFontEntry(); |
|
995 } |
|
996 |
|
997 return nullptr; |
|
998 } |
|
999 |
|
1000 void |
|
1001 gfxUserFontSet::UserFontCache::Shutdown() |
|
1002 { |
|
1003 if (sUserFonts) { |
|
1004 delete sUserFonts; |
|
1005 sUserFonts = nullptr; |
|
1006 } |
|
1007 } |
|
1008 |
|
1009 #ifdef DEBUG_USERFONT_CACHE |
|
1010 |
|
1011 PLDHashOperator |
|
1012 gfxUserFontSet::UserFontCache::Entry::DumpEntry(Entry* aEntry, void* aUserData) |
|
1013 { |
|
1014 nsresult rv; |
|
1015 |
|
1016 nsAutoCString principalURISpec; |
|
1017 |
|
1018 nsCOMPtr<nsIURI> principalURI; |
|
1019 rv = aEntry->mPrincipal->GetURI(getter_AddRefs(principalURI)); |
|
1020 if (NS_SUCCEEDED(rv)) { |
|
1021 principalURI->GetSpec(principalURISpec); |
|
1022 } |
|
1023 |
|
1024 bool setDomain = false; |
|
1025 nsCOMPtr<nsIURI> domainURI; |
|
1026 |
|
1027 aEntry->mPrincipal->GetDomain(getter_AddRefs(domainURI)); |
|
1028 if (domainURI) { |
|
1029 setDomain = true; |
|
1030 } |
|
1031 |
|
1032 NS_ASSERTION(aEntry->mURI, "null URI in userfont cache entry"); |
|
1033 |
|
1034 printf("userfontcache fontEntry: %p fonturihash: %8.8x family: %s domainset: %s principal: [%s]\n", |
|
1035 aEntry->mFontEntry, |
|
1036 nsURIHashKey::HashKey(aEntry->mURI), |
|
1037 NS_ConvertUTF16toUTF8(aEntry->mFontEntry->FamilyName()).get(), |
|
1038 (setDomain ? "true" : "false"), |
|
1039 principalURISpec.get() |
|
1040 ); |
|
1041 return PL_DHASH_NEXT; |
|
1042 } |
|
1043 |
|
1044 void |
|
1045 gfxUserFontSet::UserFontCache::Dump() |
|
1046 { |
|
1047 if (!sUserFonts) { |
|
1048 return; |
|
1049 } |
|
1050 |
|
1051 printf("userfontcache dump count: %d ========\n", sUserFonts->Count()); |
|
1052 sUserFonts->EnumerateEntries(Entry::DumpEntry, nullptr); |
|
1053 printf("userfontcache dump ==================\n"); |
|
1054 } |
|
1055 |
|
1056 #endif |