1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is thebes gfx code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2008-2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * John Daggett <jdaggett@mozilla.com>
23 : * Jonathan Kew <jfkthame@gmail.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifdef MOZ_LOGGING
40 : #define FORCE_PR_LOG /* Allow logging in the release build */
41 : #endif /* MOZ_LOGGING */
42 : #include "prlog.h"
43 :
44 : #include "gfxUserFontSet.h"
45 : #include "gfxPlatform.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsUnicharUtils.h"
48 : #include "prlong.h"
49 :
50 : #include "woff.h"
51 :
52 : #include "opentype-sanitiser.h"
53 : #include "ots-memory-stream.h"
54 :
55 : using namespace mozilla;
56 :
57 : #ifdef PR_LOGGING
58 1464 : PRLogModuleInfo *gfxUserFontSet::sUserFontsLog = PR_NewLogModule("userfonts");
59 : #endif /* PR_LOGGING */
60 :
61 : #define LOG(args) PR_LOG(sUserFontsLog, PR_LOG_DEBUG, args)
62 : #define LOG_ENABLED() PR_LOG_TEST(sUserFontsLog, PR_LOG_DEBUG)
63 :
64 : static PRUint64 sFontSetGeneration = LL_INIT(0, 0);
65 :
66 : // TODO: support for unicode ranges not yet implemented
67 :
68 0 : gfxProxyFontEntry::gfxProxyFontEntry(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
69 : gfxMixedFontFamily *aFamily,
70 : PRUint32 aWeight,
71 : PRUint32 aStretch,
72 : PRUint32 aItalicStyle,
73 : const nsTArray<gfxFontFeature>& aFeatureSettings,
74 : PRUint32 aLanguageOverride,
75 : gfxSparseBitSet *aUnicodeRanges)
76 0 : : gfxFontEntry(NS_LITERAL_STRING("Proxy"), aFamily),
77 : mLoadingState(NOT_LOADING),
78 0 : mUnsupportedFormat(false)
79 : {
80 0 : mIsProxy = true;
81 0 : mSrcList = aFontFaceSrcList;
82 0 : mSrcIndex = 0;
83 0 : mWeight = aWeight;
84 0 : mStretch = aStretch;
85 0 : mItalic = (aItalicStyle & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
86 0 : mFeatureSettings.AppendElements(aFeatureSettings);
87 0 : mLanguageOverride = aLanguageOverride;
88 0 : mIsUserFont = true;
89 0 : }
90 :
91 0 : gfxProxyFontEntry::~gfxProxyFontEntry()
92 : {
93 0 : }
94 :
95 : gfxFont*
96 0 : gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
97 : {
98 : // cannot create an actual font for a proxy entry
99 0 : return nsnull;
100 : }
101 :
102 0 : gfxUserFontSet::gfxUserFontSet()
103 : {
104 0 : mFontFamilies.Init(5);
105 0 : IncrementGeneration();
106 0 : }
107 :
108 0 : gfxUserFontSet::~gfxUserFontSet()
109 : {
110 0 : }
111 :
112 : gfxFontEntry*
113 0 : gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
114 : const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
115 : PRUint32 aWeight,
116 : PRUint32 aStretch,
117 : PRUint32 aItalicStyle,
118 : const nsString& aFeatureSettings,
119 : const nsString& aLanguageOverride,
120 : gfxSparseBitSet *aUnicodeRanges)
121 : {
122 0 : gfxProxyFontEntry *proxyEntry = nsnull;
123 :
124 0 : nsAutoString key(aFamilyName);
125 0 : ToLowerCase(key);
126 :
127 : bool found;
128 :
129 0 : if (aWeight == 0)
130 0 : aWeight = FONT_WEIGHT_NORMAL;
131 :
132 : // stretch, italic/oblique ==> zero implies normal
133 :
134 0 : gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found);
135 0 : if (!family) {
136 0 : family = new gfxMixedFontFamily(aFamilyName);
137 0 : mFontFamilies.Put(key, family);
138 : }
139 :
140 : // construct a new face and add it into the family
141 0 : nsTArray<gfxFontFeature> featureSettings;
142 : gfxFontStyle::ParseFontFeatureSettings(aFeatureSettings,
143 0 : featureSettings);
144 : PRUint32 languageOverride =
145 0 : gfxFontStyle::ParseFontLanguageOverride(aLanguageOverride);
146 : proxyEntry =
147 : new gfxProxyFontEntry(aFontFaceSrcList, family, aWeight, aStretch,
148 : aItalicStyle,
149 : featureSettings,
150 : languageOverride,
151 0 : aUnicodeRanges);
152 0 : family->AddFontEntry(proxyEntry);
153 : #ifdef PR_LOGGING
154 0 : if (LOG_ENABLED()) {
155 0 : LOG(("userfonts (%p) added (%s) with style: %s weight: %d stretch: %d",
156 : this, NS_ConvertUTF16toUTF8(aFamilyName).get(),
157 : (aItalicStyle & FONT_STYLE_ITALIC ? "italic" :
158 : (aItalicStyle & FONT_STYLE_OBLIQUE ? "oblique" : "normal")),
159 : aWeight, aStretch));
160 : }
161 : #endif
162 :
163 0 : return proxyEntry;
164 : }
165 :
166 : void
167 0 : gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
168 : gfxFontEntry *aFontEntry)
169 : {
170 0 : nsAutoString key(aFamilyName);
171 0 : ToLowerCase(key);
172 :
173 : bool found;
174 :
175 0 : gfxMixedFontFamily *family = mFontFamilies.GetWeak(key, &found);
176 0 : if (!family) {
177 0 : family = new gfxMixedFontFamily(aFamilyName);
178 0 : mFontFamilies.Put(key, family);
179 : }
180 :
181 0 : family->AddFontEntry(aFontEntry);
182 0 : }
183 :
184 : gfxFontEntry*
185 0 : gfxUserFontSet::FindFontEntry(const nsAString& aName,
186 : const gfxFontStyle& aFontStyle,
187 : bool& aFoundFamily,
188 : bool& aNeedsBold,
189 : bool& aWaitForUserFont)
190 : {
191 0 : aWaitForUserFont = false;
192 0 : gfxMixedFontFamily *family = GetFamily(aName);
193 :
194 : // no user font defined for this name
195 0 : if (!family) {
196 0 : aFoundFamily = false;
197 0 : return nsnull;
198 : }
199 :
200 0 : aFoundFamily = true;
201 0 : gfxFontEntry* fe = family->FindFontForStyle(aFontStyle, aNeedsBold);
202 :
203 : // if not a proxy, font has already been loaded
204 0 : if (!fe->mIsProxy) {
205 0 : return fe;
206 : }
207 :
208 0 : gfxProxyFontEntry *proxyEntry = static_cast<gfxProxyFontEntry*> (fe);
209 :
210 : // if currently loading, return null for now
211 0 : if (proxyEntry->mLoadingState > gfxProxyFontEntry::NOT_LOADING) {
212 : aWaitForUserFont =
213 0 : (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY);
214 0 : return nsnull;
215 : }
216 :
217 : // hasn't been loaded yet, start the load process
218 : LoadStatus status;
219 :
220 : // NOTE that if all sources in the entry fail, this will delete proxyEntry,
221 : // so we cannot use it again if status==STATUS_END_OF_LIST
222 0 : status = LoadNext(proxyEntry);
223 :
224 : // if the load succeeded immediately, the font entry was replaced so
225 : // search again
226 0 : if (status == STATUS_LOADED) {
227 0 : return family->FindFontForStyle(aFontStyle, aNeedsBold);
228 : }
229 :
230 : // check whether we should wait for load to complete before painting
231 : // a fallback font -- but not if all sources failed (bug 633500)
232 : aWaitForUserFont = (status != STATUS_END_OF_LIST) &&
233 0 : (proxyEntry->mLoadingState < gfxProxyFontEntry::LOADING_SLOWLY);
234 :
235 : // if either loading or an error occurred, return null
236 0 : return nsnull;
237 : }
238 :
239 : // Given a buffer of downloaded font data, do any necessary preparation
240 : // to make it into usable OpenType.
241 : // May return the original pointer unchanged, or a newly-allocated
242 : // block (in which case the passed-in block is NS_Free'd).
243 : // aLength is updated if necessary to the new length of the data.
244 : // Returns NULL and NS_Free's the incoming data in case of errors.
245 : static const PRUint8*
246 0 : PrepareOpenTypeData(const PRUint8* aData, PRUint32* aLength)
247 : {
248 0 : switch(gfxFontUtils::DetermineFontDataType(aData, *aLength)) {
249 :
250 : case GFX_USERFONT_OPENTYPE:
251 : // nothing to do
252 0 : return aData;
253 :
254 : case GFX_USERFONT_WOFF: {
255 0 : PRUint32 status = eWOFF_ok;
256 0 : PRUint32 bufferSize = woffGetDecodedSize(aData, *aLength, &status);
257 0 : if (WOFF_FAILURE(status)) {
258 0 : break;
259 : }
260 0 : PRUint8* decodedData = static_cast<PRUint8*>(NS_Alloc(bufferSize));
261 0 : if (!decodedData) {
262 0 : break;
263 : }
264 : woffDecodeToBuffer(aData, *aLength,
265 : decodedData, bufferSize,
266 0 : aLength, &status);
267 : // replace original data with the decoded version
268 0 : NS_Free((void*)aData);
269 0 : aData = decodedData;
270 0 : if (WOFF_FAILURE(status)) {
271 : // something went wrong, discard the data and return NULL
272 0 : break;
273 : }
274 : // success, return the decoded data
275 0 : return aData;
276 : }
277 :
278 : // xxx - add support for other wrappers here
279 :
280 : default:
281 0 : NS_WARNING("unknown font format");
282 0 : break;
283 : }
284 :
285 : // discard downloaded data that couldn't be used
286 0 : NS_Free((void*)aData);
287 :
288 0 : return nsnull;
289 : }
290 :
291 : // Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
292 : // adapted to use Mozilla allocators and to allow the final
293 : // memory buffer to be adopted by the client.
294 : class ExpandingMemoryStream : public ots::OTSStream {
295 : public:
296 0 : ExpandingMemoryStream(size_t initial, size_t limit)
297 0 : : mLength(initial), mLimit(limit), mOff(0) {
298 0 : mPtr = NS_Alloc(mLength);
299 0 : }
300 :
301 0 : ~ExpandingMemoryStream() {
302 0 : NS_Free(mPtr);
303 0 : }
304 :
305 : // return the buffer, and give up ownership of it
306 : // so the caller becomes responsible to call NS_Free
307 : // when finished with it
308 0 : void* forget() {
309 0 : void* p = mPtr;
310 0 : mPtr = nsnull;
311 0 : return p;
312 : }
313 :
314 0 : bool WriteRaw(const void *data, size_t length) {
315 0 : if ((mOff + length > mLength) ||
316 0 : (mLength > std::numeric_limits<size_t>::max() - mOff)) {
317 0 : if (mLength == mLimit) {
318 0 : return false;
319 : }
320 0 : size_t newLength = (mLength + 1) * 2;
321 0 : if (newLength < mLength) {
322 0 : return false;
323 : }
324 0 : if (newLength > mLimit) {
325 0 : newLength = mLimit;
326 : }
327 0 : mPtr = NS_Realloc(mPtr, newLength);
328 0 : mLength = newLength;
329 0 : return WriteRaw(data, length);
330 : }
331 0 : std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
332 0 : mOff += length;
333 0 : return true;
334 : }
335 :
336 0 : bool Seek(off_t position) {
337 0 : if (position < 0) {
338 0 : return false;
339 : }
340 0 : if (static_cast<size_t>(position) > mLength) {
341 0 : return false;
342 : }
343 0 : mOff = position;
344 0 : return true;
345 : }
346 :
347 0 : off_t Tell() const {
348 0 : return mOff;
349 : }
350 :
351 : private:
352 : void* mPtr;
353 : size_t mLength;
354 : const size_t mLimit;
355 : off_t mOff;
356 : };
357 :
358 : // Call the OTS library to sanitize an sfnt before attempting to use it.
359 : // Returns a newly-allocated block, or NULL in case of fatal errors.
360 : static const PRUint8*
361 0 : SanitizeOpenTypeData(const PRUint8* aData, PRUint32 aLength,
362 : PRUint32& aSaneLength, bool aIsCompressed)
363 : {
364 : // limit output/expansion to 256MB
365 : ExpandingMemoryStream output(aIsCompressed ? aLength * 2 : aLength,
366 0 : 1024 * 1024 * 256);
367 : #ifdef MOZ_GRAPHITE
368 : #define PRESERVE_GRAPHITE true
369 : #else
370 : #define PRESERVE_GRAPHITE false
371 : #endif
372 0 : if (ots::Process(&output, aData, aLength, PRESERVE_GRAPHITE)) {
373 0 : aSaneLength = output.Tell();
374 0 : return static_cast<PRUint8*>(output.forget());
375 : } else {
376 0 : aSaneLength = 0;
377 0 : return nsnull;
378 : }
379 : }
380 :
381 : static void
382 0 : StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy,
383 : const nsAString& aOriginalName,
384 : nsTArray<PRUint8>* aMetadata, PRUint32 aMetaOrigLen)
385 : {
386 0 : if (!aFontEntry->mUserFontData) {
387 0 : aFontEntry->mUserFontData = new gfxUserFontData;
388 : }
389 0 : gfxUserFontData* userFontData = aFontEntry->mUserFontData;
390 0 : userFontData->mSrcIndex = aProxy->mSrcIndex;
391 0 : const gfxFontFaceSrc& src = aProxy->mSrcList[aProxy->mSrcIndex];
392 0 : if (src.mIsLocal) {
393 0 : userFontData->mLocalName = src.mLocalName;
394 : } else {
395 0 : userFontData->mURI = src.mURI;
396 : }
397 0 : userFontData->mFormat = src.mFormatFlags;
398 0 : userFontData->mRealName = aOriginalName;
399 0 : if (aMetadata) {
400 0 : userFontData->mMetadata.SwapElements(*aMetadata);
401 0 : userFontData->mMetaOrigLen = aMetaOrigLen;
402 : }
403 0 : }
404 :
405 : struct WOFFHeader {
406 : AutoSwap_PRUint32 signature;
407 : AutoSwap_PRUint32 flavor;
408 : AutoSwap_PRUint32 length;
409 : AutoSwap_PRUint16 numTables;
410 : AutoSwap_PRUint16 reserved;
411 : AutoSwap_PRUint32 totalSfntSize;
412 : AutoSwap_PRUint16 majorVersion;
413 : AutoSwap_PRUint16 minorVersion;
414 : AutoSwap_PRUint32 metaOffset;
415 : AutoSwap_PRUint32 metaCompLen;
416 : AutoSwap_PRUint32 metaOrigLen;
417 : AutoSwap_PRUint32 privOffset;
418 : AutoSwap_PRUint32 privLen;
419 : };
420 :
421 : void
422 0 : gfxUserFontSet::CopyWOFFMetadata(const PRUint8* aFontData,
423 : PRUint32 aLength,
424 : nsTArray<PRUint8>* aMetadata,
425 : PRUint32* aMetaOrigLen)
426 : {
427 : // This function may be called with arbitrary, unvalidated "font" data
428 : // from @font-face, so it needs to be careful to bounds-check, etc.,
429 : // before trying to read anything.
430 : // This just saves a copy of the compressed data block; it does NOT check
431 : // that the block can be successfully decompressed, or that it contains
432 : // well-formed/valid XML metadata.
433 0 : if (aLength < sizeof(WOFFHeader)) {
434 0 : return;
435 : }
436 0 : const WOFFHeader* woff = reinterpret_cast<const WOFFHeader*>(aFontData);
437 0 : PRUint32 metaOffset = woff->metaOffset;
438 0 : PRUint32 metaCompLen = woff->metaCompLen;
439 0 : if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
440 0 : return;
441 : }
442 0 : if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
443 0 : return;
444 : }
445 0 : if (!aMetadata->SetLength(woff->metaCompLen)) {
446 0 : return;
447 : }
448 0 : memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
449 0 : *aMetaOrigLen = woff->metaOrigLen;
450 : }
451 :
452 : // This is called when a font download finishes.
453 : // Ownership of aFontData passes in here, and the font set must
454 : // ensure that it is eventually deleted via NS_Free().
455 : bool
456 0 : gfxUserFontSet::OnLoadComplete(gfxProxyFontEntry *aProxy,
457 : const PRUint8 *aFontData, PRUint32 aLength,
458 : nsresult aDownloadStatus)
459 : {
460 : // download successful, make platform font using font data
461 0 : if (NS_SUCCEEDED(aDownloadStatus)) {
462 :
463 : // if the proxy doesn't belong to a family, we just bail as it won't be
464 : // accessible/usable anyhow (maybe the font set got modified right as
465 : // the load was completing?)
466 0 : if (!aProxy->Family()) {
467 0 : NS_Free(const_cast<PRUint8*>(aFontData));
468 0 : return true;
469 : }
470 :
471 0 : gfxFontEntry *fe = nsnull;
472 :
473 : gfxUserFontType fontType =
474 0 : gfxFontUtils::DetermineFontDataType(aFontData, aLength);
475 :
476 : // Save a copy of the metadata block (if present) for nsIDOMFontFace
477 : // to use if required. Ownership of the metadata block will be passed
478 : // to the gfxUserFontData record below.
479 : // NOTE: after the non-OTS codepath using PrepareOpenTypeData is
480 : // removed, we should defer this until after we've created the new
481 : // fontEntry.
482 0 : nsTArray<PRUint8> metadata;
483 0 : PRUint32 metaOrigLen = 0;
484 0 : if (fontType == GFX_USERFONT_WOFF) {
485 0 : CopyWOFFMetadata(aFontData, aLength, &metadata, &metaOrigLen);
486 : }
487 :
488 : // Unwrap/decompress/sanitize or otherwise munge the downloaded data
489 : // to make a usable sfnt structure.
490 :
491 : // Because platform font activation code may replace the name table
492 : // in the font with a synthetic one, we save the original name so that
493 : // it can be reported via the nsIDOMFontFace API.
494 0 : nsAutoString originalFullName;
495 :
496 0 : if (gfxPlatform::GetPlatform()->SanitizeDownloadedFonts()) {
497 : // Call the OTS sanitizer; this will also decode WOFF to sfnt
498 : // if necessary. The original data in aFontData is left unchanged.
499 : PRUint32 saneLen;
500 : const PRUint8* saneData =
501 : SanitizeOpenTypeData(aFontData, aLength, saneLen,
502 0 : fontType == GFX_USERFONT_WOFF);
503 0 : if (!saneData) {
504 0 : LogMessage(aProxy, "rejected by sanitizer");
505 : }
506 0 : if (saneData) {
507 : // The sanitizer ensures that we have a valid sfnt and a usable
508 : // name table, so this should never fail unless we're out of
509 : // memory, and GetFullNameFromSFNT is not directly exposed to
510 : // arbitrary/malicious data from the web.
511 : gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
512 0 : originalFullName);
513 : // Here ownership of saneData is passed to the platform,
514 : // which will delete it when no longer required
515 0 : fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
516 : saneData,
517 0 : saneLen);
518 0 : if (!fe) {
519 0 : LogMessage(aProxy, "not usable by platform");
520 : }
521 : }
522 : } else {
523 : // FIXME: this code can be removed once we remove the pref to
524 : // disable the sanitizer; the PrepareOpenTypeData and
525 : // ValidateSFNTHeaders functions will then be obsolete.
526 0 : aFontData = PrepareOpenTypeData(aFontData, &aLength);
527 :
528 0 : if (aFontData) {
529 0 : if (gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
530 : // ValidateSFNTHeaders has checked that we have a valid
531 : // sfnt structure and a usable 'name' table
532 : gfxFontUtils::GetFullNameFromSFNT(aFontData, aLength,
533 0 : originalFullName);
534 : // Here ownership of aFontData is passed to the platform,
535 : // which will delete it when no longer required
536 0 : fe = gfxPlatform::GetPlatform()->MakePlatformFont(aProxy,
537 : aFontData,
538 0 : aLength);
539 0 : if (!fe) {
540 0 : LogMessage(aProxy, "not usable by platform");
541 : }
542 0 : aFontData = nsnull; // we must NOT free this below!
543 : } else {
544 : // the data was unusable, so just discard it
545 : // (error will be reported below, if logging is enabled)
546 0 : LogMessage(aProxy, "SFNT header or tables invalid");
547 : }
548 : }
549 : }
550 :
551 0 : if (aFontData) {
552 0 : NS_Free((void*)aFontData);
553 0 : aFontData = nsnull;
554 : }
555 :
556 0 : if (fe) {
557 : // copy OpenType feature/language settings from the proxy to the
558 : // newly-created font entry
559 0 : fe->mFeatureSettings.AppendElements(aProxy->mFeatureSettings);
560 0 : fe->mLanguageOverride = aProxy->mLanguageOverride;
561 : StoreUserFontData(fe, aProxy, originalFullName,
562 0 : &metadata, metaOrigLen);
563 : #ifdef PR_LOGGING
564 : // must do this before ReplaceFontEntry() because that will
565 : // clear the proxy's mFamily pointer!
566 0 : if (LOG_ENABLED()) {
567 0 : nsCAutoString fontURI;
568 0 : aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
569 0 : LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) gen: %8.8x\n",
570 : this, aProxy->mSrcIndex, fontURI.get(),
571 : NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get(),
572 : PRUint32(mGeneration)));
573 : }
574 : #endif
575 0 : ReplaceFontEntry(aProxy, fe);
576 0 : IncrementGeneration();
577 0 : return true;
578 : } else {
579 : #ifdef PR_LOGGING
580 0 : if (LOG_ENABLED()) {
581 0 : nsCAutoString fontURI;
582 0 : aProxy->mSrcList[aProxy->mSrcIndex].mURI->GetSpec(fontURI);
583 0 : LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s) error making platform font\n",
584 : this, aProxy->mSrcIndex, fontURI.get(),
585 : NS_ConvertUTF16toUTF8(aProxy->mFamily->Name()).get()));
586 : }
587 : #endif
588 : }
589 : } else {
590 : // download failed
591 : LogMessage(aProxy, "download failed", nsIScriptError::errorFlag,
592 0 : aDownloadStatus);
593 : }
594 :
595 0 : if (aFontData) {
596 0 : NS_Free((void*)aFontData);
597 : }
598 :
599 : // error occurred, load next src
600 0 : (void)LoadNext(aProxy);
601 :
602 : // We ignore the status returned by LoadNext();
603 : // even if loading failed, we need to bump the font-set generation
604 : // and return true in order to trigger reflow, so that fallback
605 : // will be used where the text was "masked" by the pending download
606 0 : IncrementGeneration();
607 0 : return true;
608 : }
609 :
610 :
611 : gfxUserFontSet::LoadStatus
612 0 : gfxUserFontSet::LoadNext(gfxProxyFontEntry *aProxyEntry)
613 : {
614 0 : PRUint32 numSrc = aProxyEntry->mSrcList.Length();
615 :
616 0 : NS_ASSERTION(aProxyEntry->mSrcIndex < numSrc,
617 : "already at the end of the src list for user font");
618 :
619 0 : if (aProxyEntry->mLoadingState == gfxProxyFontEntry::NOT_LOADING) {
620 0 : aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_STARTED;
621 0 : aProxyEntry->mUnsupportedFormat = false;
622 : } else {
623 : // we were already loading; move to the next source,
624 : // but don't reset state - if we've already timed out,
625 : // that counts against the new download
626 0 : aProxyEntry->mSrcIndex++;
627 : }
628 :
629 : // load each src entry in turn, until a local face is found
630 : // or a download begins successfully
631 0 : while (aProxyEntry->mSrcIndex < numSrc) {
632 0 : const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex];
633 :
634 : // src local ==> lookup and load
635 :
636 0 : if (currSrc.mIsLocal) {
637 : gfxFontEntry *fe =
638 0 : gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry,
639 0 : currSrc.mLocalName);
640 0 : if (fe) {
641 0 : LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
642 : this, aProxyEntry->mSrcIndex,
643 : NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
644 : NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get(),
645 : PRUint32(mGeneration)));
646 0 : fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
647 0 : fe->mLanguageOverride = aProxyEntry->mLanguageOverride;
648 0 : StoreUserFontData(fe, aProxyEntry, nsString(), nsnull, 0);
649 0 : ReplaceFontEntry(aProxyEntry, fe);
650 0 : return STATUS_LOADED;
651 : } else {
652 0 : LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
653 : this, aProxyEntry->mSrcIndex,
654 : NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
655 : NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
656 : }
657 : }
658 :
659 : // src url ==> start the load process
660 : else {
661 0 : if (gfxPlatform::GetPlatform()->IsFontFormatSupported(currSrc.mURI,
662 0 : currSrc.mFormatFlags)) {
663 0 : nsresult rv = StartLoad(aProxyEntry, &currSrc);
664 0 : bool loadOK = NS_SUCCEEDED(rv);
665 0 : if (loadOK) {
666 : #ifdef PR_LOGGING
667 0 : if (LOG_ENABLED()) {
668 0 : nsCAutoString fontURI;
669 0 : currSrc.mURI->GetSpec(fontURI);
670 0 : LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
671 : this, aProxyEntry->mSrcIndex, fontURI.get(),
672 : NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
673 : }
674 : #endif
675 0 : return STATUS_LOADING;
676 : } else {
677 : LogMessage(aProxyEntry, "download failed",
678 0 : nsIScriptError::errorFlag, rv);
679 : }
680 : } else {
681 : // We don't log a warning to the web console yet,
682 : // as another source may load successfully
683 0 : aProxyEntry->mUnsupportedFormat = true;
684 : }
685 : }
686 :
687 0 : aProxyEntry->mSrcIndex++;
688 : }
689 :
690 0 : if (aProxyEntry->mUnsupportedFormat) {
691 : LogMessage(aProxyEntry, "no supported format found",
692 0 : nsIScriptError::warningFlag);
693 : }
694 :
695 : // all src's failed; mark this entry as unusable (so fallback will occur)
696 0 : LOG(("userfonts (%p) failed all src for (%s)\n",
697 : this, NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get()));
698 0 : aProxyEntry->mLoadingState = gfxProxyFontEntry::LOADING_FAILED;
699 :
700 0 : return STATUS_END_OF_LIST;
701 : }
702 :
703 : void
704 0 : gfxUserFontSet::IncrementGeneration()
705 : {
706 : // add one, increment again if zero
707 0 : LL_ADD(sFontSetGeneration, sFontSetGeneration, 1);
708 0 : if (LL_IS_ZERO(sFontSetGeneration))
709 0 : LL_ADD(sFontSetGeneration, sFontSetGeneration, 1);
710 0 : mGeneration = sFontSetGeneration;
711 0 : }
712 :
713 :
714 : gfxMixedFontFamily*
715 0 : gfxUserFontSet::GetFamily(const nsAString& aFamilyName) const
716 : {
717 0 : nsAutoString key(aFamilyName);
718 0 : ToLowerCase(key);
719 :
720 0 : return mFontFamilies.GetWeak(key);
721 4392 : }
|