gfx/thebes/gfxUserFontSet.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5f8d8ad6ff16
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

mercurial