Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxUserFontSet.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "mozilla/Logging.h"
7
8
#include "gfxUserFontSet.h"
9
#include "gfxPlatform.h"
10
#include "gfxPrefs.h"
11
#include "nsIProtocolHandler.h"
12
#include "gfxFontConstants.h"
13
#include "mozilla/FontPropertyTypes.h"
14
#include "mozilla/Preferences.h"
15
#include "mozilla/Services.h"
16
#include "mozilla/Telemetry.h"
17
#include "mozilla/gfx/2D.h"
18
#include "gfxPlatformFontList.h"
19
#include "mozilla/ServoStyleSet.h"
20
#include "mozilla/PostTraversalTask.h"
21
22
#include "opentype-sanitiser.h"
23
#include "ots-memory-stream.h"
24
25
using namespace mozilla;
26
27
mozilla::LogModule*
28
gfxUserFontSet::GetUserFontsLog()
29
0
{
30
0
    static LazyLogModule sLog("userfonts");
31
0
    return sLog;
32
0
}
33
34
0
#define LOG(args) MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
35
0
#define LOG_ENABLED() MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug)
36
37
static uint64_t sFontSetGeneration = 0;
38
39
// Based on ots::ExpandingMemoryStream from ots-memory-stream.h,
40
// adapted to use Mozilla allocators and to allow the final
41
// memory buffer to be adopted by the client.
42
class ExpandingMemoryStream : public ots::OTSStream {
43
public:
44
    ExpandingMemoryStream(size_t initial, size_t limit)
45
0
        : mLength(initial), mLimit(limit), mOff(0) {
46
0
        mPtr = moz_xmalloc(mLength);
47
0
    }
48
49
0
    ~ExpandingMemoryStream() {
50
0
        free(mPtr);
51
0
    }
52
53
    // Return the buffer, resized to fit its contents (as it may have been
54
    // over-allocated during growth), and give up ownership of it so the
55
    // caller becomes responsible to call free() when finished with it.
56
0
    void* forget() {
57
0
        void* p = moz_xrealloc(mPtr, mOff);
58
0
        mPtr = nullptr;
59
0
        return p;
60
0
    }
61
62
0
    bool WriteRaw(const void* data, size_t length) override {
63
0
        if ((mOff + length > mLength) ||
64
0
            (mLength > std::numeric_limits<size_t>::max() - mOff)) {
65
0
            if (mLength == mLimit) {
66
0
                return false;
67
0
            }
68
0
            size_t newLength = (mLength + 1) * 2;
69
0
            if (newLength < mLength) {
70
0
                return false;
71
0
            }
72
0
            if (newLength > mLimit) {
73
0
                newLength = mLimit;
74
0
            }
75
0
            mPtr = moz_xrealloc(mPtr, newLength);
76
0
            mLength = newLength;
77
0
            return WriteRaw(data, length);
78
0
        }
79
0
        std::memcpy(static_cast<char*>(mPtr) + mOff, data, length);
80
0
        mOff += length;
81
0
        return true;
82
0
    }
83
84
0
    bool Seek(off_t position) override {
85
0
        if (position < 0) {
86
0
            return false;
87
0
        }
88
0
        if (static_cast<size_t>(position) > mLength) {
89
0
            return false;
90
0
        }
91
0
        mOff = position;
92
0
        return true;
93
0
    }
94
95
0
    off_t Tell() const override {
96
0
        return mOff;
97
0
    }
98
99
private:
100
    void*        mPtr;
101
    size_t       mLength;
102
    const size_t mLimit;
103
    off_t        mOff;
104
};
105
106
gfxUserFontEntry::gfxUserFontEntry(gfxUserFontSet* aFontSet,
107
             const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
108
             WeightRange aWeight,
109
             StretchRange aStretch,
110
             SlantStyleRange aStyle,
111
             const nsTArray<gfxFontFeature>& aFeatureSettings,
112
             const nsTArray<gfxFontVariation>& aVariationSettings,
113
             uint32_t aLanguageOverride,
114
             gfxCharacterMap* aUnicodeRanges,
115
             uint8_t aFontDisplay,
116
             RangeFlags aRangeFlags)
117
    : gfxFontEntry(NS_LITERAL_CSTRING("userfont")),
118
      mUserFontLoadState(STATUS_NOT_LOADED),
119
      mFontDataLoadingState(NOT_LOADING),
120
      mUnsupportedFormat(false),
121
      mFontDisplay(aFontDisplay),
122
      mLoader(nullptr),
123
      mFontSet(aFontSet)
124
0
{
125
0
    mIsUserFontContainer = true;
126
0
    mSrcList = aFontFaceSrcList;
127
0
    mSrcIndex = 0;
128
0
    mWeightRange = aWeight;
129
0
    mStretchRange = aStretch;
130
0
    mStyleRange = aStyle;
131
0
    mFeatureSettings.AppendElements(aFeatureSettings);
132
0
    mVariationSettings.AppendElements(aVariationSettings);
133
0
    mLanguageOverride = aLanguageOverride;
134
0
    mCharacterMap = aUnicodeRanges;
135
0
    mRangeFlags = aRangeFlags;
136
0
}
137
138
gfxUserFontEntry::~gfxUserFontEntry()
139
0
{
140
0
    // Assert that we don't drop any gfxUserFontEntry objects during a Servo
141
0
    // traversal, since PostTraversalTask objects can hold raw pointers to
142
0
    // gfxUserFontEntry objects.
143
0
    MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
144
0
}
145
146
bool
147
gfxUserFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
148
                          WeightRange aWeight,
149
                          StretchRange aStretch,
150
                          SlantStyleRange aStyle,
151
                          const nsTArray<gfxFontFeature>& aFeatureSettings,
152
                          const nsTArray<gfxFontVariation>& aVariationSettings,
153
                          uint32_t aLanguageOverride,
154
                          gfxCharacterMap* aUnicodeRanges,
155
                          uint8_t aFontDisplay,
156
                          RangeFlags aRangeFlags)
157
0
{
158
0
    return Weight() == aWeight &&
159
0
           Stretch() == aStretch &&
160
0
           SlantStyle() == aStyle &&
161
0
           mFeatureSettings == aFeatureSettings &&
162
0
           mVariationSettings == aVariationSettings &&
163
0
           mLanguageOverride == aLanguageOverride &&
164
0
           mSrcList == aFontFaceSrcList &&
165
0
           mFontDisplay == aFontDisplay &&
166
0
           mRangeFlags == aRangeFlags &&
167
0
           ((!aUnicodeRanges && !mCharacterMap) ||
168
0
            (aUnicodeRanges && mCharacterMap && mCharacterMap->Equals(aUnicodeRanges)));
169
0
}
170
171
gfxFont*
172
gfxUserFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle)
173
0
{
174
0
    MOZ_ASSERT_UNREACHABLE("should only be creating a gfxFont"
175
0
                           " with an actual platform font entry");
176
0
177
0
    // userfont entry is a container, can't create font from the container
178
0
    return nullptr;
179
0
}
180
181
class MOZ_STACK_CLASS gfxOTSContext : public ots::OTSContext {
182
public:
183
    explicit gfxOTSContext(gfxUserFontEntry* aUserFontEntry)
184
        : mUserFontEntry(aUserFontEntry)
185
0
    {
186
0
        // Whether to apply OTS validation to OpenType Layout tables
187
0
        mCheckOTLTables = gfxPrefs::ValidateOTLTables();
188
0
        // Whether to preserve Variation tables in downloaded fonts
189
0
        mCheckVariationTables = gfxPrefs::ValidateVariationTables();
190
0
        // Whether to preserve color bitmap glyphs
191
0
        mKeepColorBitmaps = gfxPrefs::KeepColorBitmaps();
192
0
    }
193
194
0
    virtual ots::TableAction GetTableAction(uint32_t aTag) override {
195
0
        // Preserve Graphite, color glyph and SVG tables,
196
0
        // and possibly OTL and Variation tables (depending on prefs)
197
0
        if ((!mCheckOTLTables &&
198
0
             (aTag == TRUETYPE_TAG('G', 'D', 'E', 'F') ||
199
0
              aTag == TRUETYPE_TAG('G', 'P', 'O', 'S') ||
200
0
              aTag == TRUETYPE_TAG('G', 'S', 'U', 'B'))) ||
201
0
            (!mCheckVariationTables &&
202
0
             (aTag == TRUETYPE_TAG('a', 'v', 'a', 'r') ||
203
0
              aTag == TRUETYPE_TAG('c', 'v', 'a', 'r') ||
204
0
              aTag == TRUETYPE_TAG('f', 'v', 'a', 'r') ||
205
0
              aTag == TRUETYPE_TAG('g', 'v', 'a', 'r') ||
206
0
              aTag == TRUETYPE_TAG('H', 'V', 'A', 'R') ||
207
0
              aTag == TRUETYPE_TAG('M', 'V', 'A', 'R') ||
208
0
              aTag == TRUETYPE_TAG('S', 'T', 'A', 'T') ||
209
0
              aTag == TRUETYPE_TAG('V', 'V', 'A', 'R'))) ||
210
0
            aTag == TRUETYPE_TAG('S', 'V', 'G', ' ') ||
211
0
            aTag == TRUETYPE_TAG('C', 'O', 'L', 'R') ||
212
0
            aTag == TRUETYPE_TAG('C', 'P', 'A', 'L') ||
213
0
            (mKeepColorBitmaps &&
214
0
             (aTag == TRUETYPE_TAG('C', 'B', 'D', 'T') ||
215
0
              aTag == TRUETYPE_TAG('C', 'B', 'L', 'C'))) ||
216
0
            false) {
217
0
            return ots::TABLE_ACTION_PASSTHRU;
218
0
        }
219
0
        return ots::TABLE_ACTION_DEFAULT;
220
0
    }
221
222
    virtual void Message(int level, const char* format,
223
0
                         ...) MSGFUNC_FMT_ATTR override {
224
0
        va_list va;
225
0
        va_start(va, format);
226
0
227
0
        nsCString msg;
228
0
        msg.AppendPrintf(format, va);
229
0
230
0
        va_end(va);
231
0
232
0
        if (level > 0) {
233
0
            // For warnings (rather than errors that cause the font to fail),
234
0
            // we only report the first instance of any given message.
235
0
            if (mWarningsIssued.Contains(msg)) {
236
0
                return;
237
0
            }
238
0
            mWarningsIssued.PutEntry(msg);
239
0
        }
240
0
241
0
        mUserFontEntry->mFontSet->LogMessage(mUserFontEntry, msg.get());
242
0
    }
243
244
private:
245
    gfxUserFontEntry* mUserFontEntry;
246
    nsTHashtable<nsCStringHashKey> mWarningsIssued;
247
    bool mCheckOTLTables;
248
    bool mCheckVariationTables;
249
    bool mKeepColorBitmaps;
250
};
251
252
// Call the OTS library to sanitize an sfnt before attempting to use it.
253
// Returns a newly-allocated block, or nullptr in case of fatal errors.
254
const uint8_t*
255
gfxUserFontEntry::SanitizeOpenTypeData(const uint8_t* aData,
256
                                       uint32_t       aLength,
257
                                       uint32_t&      aSaneLength,
258
                                       gfxUserFontType aFontType)
259
0
{
260
0
    if (aFontType == GFX_USERFONT_UNKNOWN) {
261
0
        aSaneLength = 0;
262
0
        return nullptr;
263
0
    }
264
0
265
0
    uint32_t lengthHint = aLength;
266
0
    if (aFontType == GFX_USERFONT_WOFF) {
267
0
        lengthHint *= 2;
268
0
    } else if (aFontType == GFX_USERFONT_WOFF2) {
269
0
        lengthHint *= 3;
270
0
    }
271
0
272
0
    // limit output/expansion to 256MB
273
0
    ExpandingMemoryStream output(lengthHint, 1024 * 1024 * 256);
274
0
275
0
    gfxOTSContext otsContext(this);
276
0
    if (!otsContext.Process(&output, aData, aLength)) {
277
0
        // Failed to decode/sanitize the font, so discard it.
278
0
        aSaneLength = 0;
279
0
        return nullptr;
280
0
    }
281
0
282
0
    aSaneLength = output.Tell();
283
0
    return static_cast<const uint8_t*>(output.forget());
284
0
}
285
286
void
287
gfxUserFontEntry::StoreUserFontData(gfxFontEntry* aFontEntry,
288
                                    bool aPrivate,
289
                                    const nsACString& aOriginalName,
290
                                    FallibleTArray<uint8_t>* aMetadata,
291
                                    uint32_t aMetaOrigLen,
292
                                    uint8_t aCompression)
293
0
{
294
0
    if (!aFontEntry->mUserFontData) {
295
0
        aFontEntry->mUserFontData = MakeUnique<gfxUserFontData>();
296
0
    }
297
0
    gfxUserFontData* userFontData = aFontEntry->mUserFontData.get();
298
0
    userFontData->mSrcIndex = mSrcIndex;
299
0
    const gfxFontFaceSrc& src = mSrcList[mSrcIndex];
300
0
    switch (src.mSourceType) {
301
0
        case gfxFontFaceSrc::eSourceType_Local:
302
0
            userFontData->mLocalName = src.mLocalName;
303
0
            break;
304
0
        case gfxFontFaceSrc::eSourceType_URL:
305
0
            userFontData->mURI = src.mURI;
306
0
            userFontData->mPrincipal = mPrincipal;
307
0
            break;
308
0
        case gfxFontFaceSrc::eSourceType_Buffer:
309
0
            userFontData->mIsBuffer = true;
310
0
            break;
311
0
    }
312
0
    userFontData->mPrivate = aPrivate;
313
0
    userFontData->mFormat = src.mFormatFlags;
314
0
    userFontData->mRealName = aOriginalName;
315
0
    if (aMetadata) {
316
0
        userFontData->mMetadata.SwapElements(*aMetadata);
317
0
        userFontData->mMetaOrigLen = aMetaOrigLen;
318
0
        userFontData->mCompression = aCompression;
319
0
    }
320
0
}
321
322
size_t
323
gfxUserFontData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
324
0
{
325
0
    return aMallocSizeOf(this)
326
0
           + mMetadata.ShallowSizeOfExcludingThis(aMallocSizeOf)
327
0
           + mLocalName.SizeOfExcludingThisIfUnshared(aMallocSizeOf)
328
0
           + mRealName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
329
0
    // Not counting mURI and mPrincipal, as those will be shared.
330
0
}
331
332
/*virtual*/
333
gfxUserFontFamily::~gfxUserFontFamily()
334
0
{
335
0
  // Should not be dropped by stylo
336
0
  MOZ_ASSERT(NS_IsMainThread());
337
0
}
338
339
gfxFontSrcPrincipal*
340
gfxFontFaceSrc::LoadPrincipal(const gfxUserFontSet& aFontSet) const
341
0
{
342
0
  MOZ_ASSERT(mSourceType == eSourceType_URL);
343
0
  if (mUseOriginPrincipal && mOriginPrincipal) {
344
0
    return mOriginPrincipal;
345
0
  }
346
0
  return aFontSet.GetStandardFontLoadPrincipal();
347
0
}
348
349
void
350
gfxUserFontEntry::GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
351
                                                nsACString& aURI)
352
0
{
353
0
  aFamilyName = mFamilyName;
354
0
355
0
  aURI.Truncate();
356
0
  if (mSrcIndex == mSrcList.Length()) {
357
0
    aURI.AppendLiteral("(end of source list)");
358
0
  } else {
359
0
    if (mSrcList[mSrcIndex].mURI) {
360
0
      mSrcList[mSrcIndex].mURI->GetSpec(aURI);
361
0
      // If the source URI was very long, elide the middle of it.
362
0
      // In principle, the byte-oriented chopping here could leave us
363
0
      // with partial UTF-8 characters at the point where we cut it,
364
0
      // but it really doesn't matter as this is just for logging.
365
0
      const uint32_t kMaxURILengthForLogging = 256;
366
0
      // UTF-8 ellipsis, with spaces to allow additional wrap opportunities
367
0
      // in the resulting log message
368
0
      const char kEllipsis[] = { ' ', '\xE2', '\x80', '\xA6', ' ' };
369
0
      if (aURI.Length() > kMaxURILengthForLogging) {
370
0
        aURI.Replace(kMaxURILengthForLogging / 2,
371
0
                     aURI.Length() - kMaxURILengthForLogging,
372
0
                     kEllipsis, ArrayLength(kEllipsis));
373
0
      }
374
0
    } else {
375
0
      aURI.AppendLiteral("(invalid URI)");
376
0
    }
377
0
  }
378
0
}
379
380
struct WOFFHeader {
381
    AutoSwap_PRUint32 signature;
382
    AutoSwap_PRUint32 flavor;
383
    AutoSwap_PRUint32 length;
384
    AutoSwap_PRUint16 numTables;
385
    AutoSwap_PRUint16 reserved;
386
    AutoSwap_PRUint32 totalSfntSize;
387
    AutoSwap_PRUint16 majorVersion;
388
    AutoSwap_PRUint16 minorVersion;
389
    AutoSwap_PRUint32 metaOffset;
390
    AutoSwap_PRUint32 metaCompLen;
391
    AutoSwap_PRUint32 metaOrigLen;
392
    AutoSwap_PRUint32 privOffset;
393
    AutoSwap_PRUint32 privLen;
394
};
395
396
struct WOFF2Header {
397
    AutoSwap_PRUint32 signature;
398
    AutoSwap_PRUint32 flavor;
399
    AutoSwap_PRUint32 length;
400
    AutoSwap_PRUint16 numTables;
401
    AutoSwap_PRUint16 reserved;
402
    AutoSwap_PRUint32 totalSfntSize;
403
    AutoSwap_PRUint32 totalCompressedSize;
404
    AutoSwap_PRUint16 majorVersion;
405
    AutoSwap_PRUint16 minorVersion;
406
    AutoSwap_PRUint32 metaOffset;
407
    AutoSwap_PRUint32 metaCompLen;
408
    AutoSwap_PRUint32 metaOrigLen;
409
    AutoSwap_PRUint32 privOffset;
410
    AutoSwap_PRUint32 privLen;
411
};
412
413
template<typename HeaderT>
414
void
415
CopyWOFFMetadata(const uint8_t* aFontData,
416
                 uint32_t aLength,
417
                 FallibleTArray<uint8_t>* aMetadata,
418
                 uint32_t* aMetaOrigLen)
419
0
{
420
0
    // This function may be called with arbitrary, unvalidated "font" data
421
0
    // from @font-face, so it needs to be careful to bounds-check, etc.,
422
0
    // before trying to read anything.
423
0
    // This just saves a copy of the compressed data block; it does NOT check
424
0
    // that the block can be successfully decompressed, or that it contains
425
0
    // well-formed/valid XML metadata.
426
0
    if (aLength < sizeof(HeaderT)) {
427
0
        return;
428
0
    }
429
0
    const HeaderT* woff =
430
0
        reinterpret_cast<const HeaderT*>(aFontData);
431
0
    uint32_t metaOffset = woff->metaOffset;
432
0
    uint32_t metaCompLen = woff->metaCompLen;
433
0
    if (!metaOffset || !metaCompLen || !woff->metaOrigLen) {
434
0
        return;
435
0
    }
436
0
    if (metaOffset >= aLength || metaCompLen > aLength - metaOffset) {
437
0
        return;
438
0
    }
439
0
    if (!aMetadata->SetLength(woff->metaCompLen, fallible)) {
440
0
        return;
441
0
    }
442
0
    memcpy(aMetadata->Elements(), aFontData + metaOffset, metaCompLen);
443
0
    *aMetaOrigLen = woff->metaOrigLen;
444
0
}
Unexecuted instantiation: void CopyWOFFMetadata<WOFFHeader>(unsigned char const*, unsigned int, FallibleTArray<unsigned char>*, unsigned int*)
Unexecuted instantiation: void CopyWOFFMetadata<WOFF2Header>(unsigned char const*, unsigned int, FallibleTArray<unsigned char>*, unsigned int*)
445
446
void
447
gfxUserFontEntry::LoadNextSrc()
448
0
{
449
0
    NS_ASSERTION(mSrcIndex < mSrcList.Length(),
450
0
                 "already at the end of the src list for user font");
451
0
    NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
452
0
                  mUserFontLoadState == STATUS_LOAD_PENDING ||
453
0
                  mUserFontLoadState == STATUS_LOADING) &&
454
0
                 mFontDataLoadingState < LOADING_FAILED,
455
0
                 "attempting to load a font that has either completed or failed");
456
0
457
0
    if (mUserFontLoadState == STATUS_NOT_LOADED) {
458
0
        SetLoadState(STATUS_LOADING);
459
0
        mFontDataLoadingState = LOADING_STARTED;
460
0
        mUnsupportedFormat = false;
461
0
    } else {
462
0
        // we were already loading; move to the next source,
463
0
        // but don't reset state - if we've already timed out,
464
0
        // that counts against the new download
465
0
        mSrcIndex++;
466
0
    }
467
0
468
0
    DoLoadNextSrc(false);
469
0
}
470
471
void
472
gfxUserFontEntry::ContinueLoad()
473
0
{
474
0
    MOZ_ASSERT(mUserFontLoadState == STATUS_LOAD_PENDING);
475
0
    MOZ_ASSERT(mSrcList[mSrcIndex].mSourceType == gfxFontFaceSrc::eSourceType_URL);
476
0
477
0
    SetLoadState(STATUS_LOADING);
478
0
    DoLoadNextSrc(true);
479
0
    if (LoadState() != STATUS_LOADING) {
480
0
      MOZ_ASSERT(mUserFontLoadState != STATUS_LOAD_PENDING,
481
0
                 "Not in parallel traversal, shouldn't get LOAD_PENDING again");
482
0
      // Loading is synchronously finished (loaded from cache or failed). We
483
0
      // need to increment the generation so that we flush the style data to
484
0
      // use the new loaded font face.
485
0
      // Without parallel traversal, we would simply get the right font data
486
0
      // after the first call to DoLoadNextSrc() in this case, so we don't need
487
0
      // to touch the generation to trigger another restyle.
488
0
      // XXX We may want to return synchronously in parallel traversal in those
489
0
      // cases as well if possible, so that we don't have an additional restyle.
490
0
      // That doesn't work currently because nsIDocument::GetDocShell (called
491
0
      // from FontFaceSet::CheckFontLoad) dereferences a weak pointer, which is
492
0
      // not allowed in parallel traversal.
493
0
      IncrementGeneration();
494
0
    }
495
0
}
496
497
static bool
498
IgnorePrincipal(gfxFontSrcURI* aURI)
499
0
{
500
0
    return aURI->InheritsSecurityContext();
501
0
}
502
503
void
504
gfxUserFontEntry::DoLoadNextSrc(bool aForceAsync)
505
0
{
506
0
    uint32_t numSrc = mSrcList.Length();
507
0
508
0
    // load each src entry in turn, until a local face is found
509
0
    // or a download begins successfully
510
0
    while (mSrcIndex < numSrc) {
511
0
        gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
512
0
513
0
        // src local ==> lookup and load immediately
514
0
515
0
        if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
516
0
            // Don't look up local fonts if the font whitelist is being used.
517
0
            gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
518
0
            gfxFontEntry* fe = pfl && pfl->IsFontFamilyWhitelistActive() ?
519
0
                nullptr :
520
0
                gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
521
0
                                                            Weight(),
522
0
                                                            Stretch(),
523
0
                                                            SlantStyle());
524
0
            nsTArray<gfxUserFontSet*> fontSets;
525
0
            GetUserFontSets(fontSets);
526
0
            for (gfxUserFontSet* fontSet : fontSets) {
527
0
                // We need to note on each gfxUserFontSet that contains the user
528
0
                // font entry that we used a local() rule.
529
0
                fontSet->SetLocalRulesUsed();
530
0
            }
531
0
            if (fe) {
532
0
                LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
533
0
                     mFontSet, mSrcIndex,
534
0
                     currSrc.mLocalName.get(),
535
0
                     mFamilyName.get(),
536
0
                     uint32_t(mFontSet->mGeneration)));
537
0
                fe->mFeatureSettings.AppendElements(mFeatureSettings);
538
0
                fe->mVariationSettings.AppendElements(mVariationSettings);
539
0
                fe->mLanguageOverride = mLanguageOverride;
540
0
                fe->mFamilyName = mFamilyName;
541
0
                fe->mRangeFlags = mRangeFlags;
542
0
                // For src:local(), we don't care whether the request is from
543
0
                // a private window as there's no issue of caching resources;
544
0
                // local fonts are just available all the time.
545
0
                StoreUserFontData(fe, false, nsCString(), nullptr, 0,
546
0
                                  gfxUserFontData::kUnknownCompression);
547
0
                mPlatformFontEntry = fe;
548
0
                SetLoadState(STATUS_LOADED);
549
0
                Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
550
0
                                      currSrc.mSourceType + 1);
551
0
                return;
552
0
            } else {
553
0
                LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
554
0
                     mFontSet, mSrcIndex,
555
0
                     currSrc.mLocalName.get(),
556
0
                     mFamilyName.get()));
557
0
            }
558
0
        }
559
0
560
0
        // src url ==> start the load process
561
0
        else if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL) {
562
0
            if (gfxPlatform::GetPlatform()->IsFontFormatSupported(
563
0
                    currSrc.mFormatFlags)) {
564
0
565
0
                if (ServoStyleSet* set = ServoStyleSet::Current()) {
566
0
                    // Only support style worker threads synchronously getting
567
0
                    // entries from the font cache when it's not a data: URI
568
0
                    // @font-face that came from UA or user sheets, since we
569
0
                    // were not able to call IsFontLoadAllowed ahead of time
570
0
                    // for these entries.
571
0
                    if (currSrc.mUseOriginPrincipal && IgnorePrincipal(currSrc.mURI)) {
572
0
                        set->AppendTask(PostTraversalTask::LoadFontEntry(this));
573
0
                        SetLoadState(STATUS_LOAD_PENDING);
574
0
                        return;
575
0
                    }
576
0
                }
577
0
578
0
                // see if we have an existing entry for this source
579
0
                gfxFontEntry* fe =
580
0
                  gfxUserFontSet::UserFontCache::GetFont(currSrc, *this);
581
0
                if (fe) {
582
0
                    mPlatformFontEntry = fe;
583
0
                    SetLoadState(STATUS_LOADED);
584
0
                    if (LOG_ENABLED()) {
585
0
                        LOG(("userfonts (%p) [src %d] "
586
0
                             "loaded uri from cache: (%s) for (%s)\n",
587
0
                             mFontSet, mSrcIndex,
588
0
                             currSrc.mURI->GetSpecOrDefault().get(),
589
0
                             mFamilyName.get()));
590
0
                    }
591
0
                    return;
592
0
                }
593
0
594
0
                if (ServoStyleSet* set = ServoStyleSet::Current()) {
595
0
                    // If we need to start a font load and we're on a style
596
0
                    // worker thread, we have to defer it.
597
0
                    set->AppendTask(PostTraversalTask::LoadFontEntry(this));
598
0
                    SetLoadState(STATUS_LOAD_PENDING);
599
0
                    return;
600
0
                }
601
0
602
0
                // record the principal we should use for the load for use when
603
0
                // creating a channel and when caching the loaded entry.
604
0
                mPrincipal = currSrc.LoadPrincipal(*mFontSet);
605
0
606
0
                bool loadDoesntSpin =
607
0
                  !aForceAsync && currSrc.mURI->SyncLoadIsOK();
608
0
609
0
                if (loadDoesntSpin) {
610
0
                    uint8_t* buffer = nullptr;
611
0
                    uint32_t bufferLength = 0;
612
0
613
0
                    // sync load font immediately
614
0
                    nsresult rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
615
0
                                                    bufferLength);
616
0
617
0
                    if (NS_SUCCEEDED(rv) &&
618
0
                        LoadPlatformFont(buffer, bufferLength)) {
619
0
                        SetLoadState(STATUS_LOADED);
620
0
                        Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
621
0
                                              currSrc.mSourceType + 1);
622
0
                        return;
623
0
                    } else {
624
0
                        mFontSet->LogMessage(this,
625
0
                                             "font load failed",
626
0
                                             nsIScriptError::errorFlag,
627
0
                                             rv);
628
0
                    }
629
0
630
0
                } else {
631
0
                    // otherwise load font async
632
0
                    nsresult rv = mFontSet->StartLoad(this, &currSrc);
633
0
                    bool loadOK = NS_SUCCEEDED(rv);
634
0
635
0
                    if (loadOK) {
636
0
                        if (LOG_ENABLED()) {
637
0
                            LOG(("userfonts (%p) [src %d] loading uri: (%s) for (%s)\n",
638
0
                                 mFontSet, mSrcIndex,
639
0
                                 currSrc.mURI->GetSpecOrDefault().get(),
640
0
                                 mFamilyName.get()));
641
0
                        }
642
0
                        return;
643
0
                    } else {
644
0
                        mFontSet->LogMessage(this,
645
0
                                             "download failed",
646
0
                                             nsIScriptError::errorFlag,
647
0
                                             rv);
648
0
                    }
649
0
                }
650
0
            } else {
651
0
                // We don't log a warning to the web console yet,
652
0
                // as another source may load successfully
653
0
                mUnsupportedFormat = true;
654
0
            }
655
0
        }
656
0
657
0
        // FontFace buffer ==> load immediately
658
0
659
0
        else {
660
0
            MOZ_ASSERT(currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Buffer);
661
0
662
0
            uint8_t* buffer = nullptr;
663
0
            uint32_t bufferLength = 0;
664
0
665
0
            // sync load font immediately
666
0
            currSrc.mBuffer->TakeBuffer(buffer, bufferLength);
667
0
            if (buffer && LoadPlatformFont(buffer, bufferLength)) {
668
0
                // LoadPlatformFont takes ownership of the buffer, so no need
669
0
                // to free it here.
670
0
                SetLoadState(STATUS_LOADED);
671
0
                Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
672
0
                                      currSrc.mSourceType + 1);
673
0
                return;
674
0
            } else {
675
0
                mFontSet->LogMessage(this,
676
0
                                     "font load failed",
677
0
                                     nsIScriptError::errorFlag);
678
0
            }
679
0
        }
680
0
681
0
        mSrcIndex++;
682
0
    }
683
0
684
0
    if (mUnsupportedFormat) {
685
0
        mFontSet->LogMessage(this, "no supported format found",
686
0
                             nsIScriptError::warningFlag);
687
0
    }
688
0
689
0
    // all src's failed; mark this entry as unusable (so fallback will occur)
690
0
    LOG(("userfonts (%p) failed all src for (%s)\n",
691
0
        mFontSet, mFamilyName.get()));
692
0
    mFontDataLoadingState = LOADING_FAILED;
693
0
    SetLoadState(STATUS_FAILED);
694
0
}
695
696
void
697
gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState)
698
0
{
699
0
    mUserFontLoadState = aLoadState;
700
0
}
701
702
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc)
703
704
bool
705
gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
706
0
{
707
0
    AUTO_PROFILER_LABEL("gfxUserFontEntry::LoadPlatformFont", OTHER);
708
0
    NS_ASSERTION((mUserFontLoadState == STATUS_NOT_LOADED ||
709
0
                  mUserFontLoadState == STATUS_LOAD_PENDING ||
710
0
                  mUserFontLoadState == STATUS_LOADING) &&
711
0
                 mFontDataLoadingState < LOADING_FAILED,
712
0
                 "attempting to load a font that has either completed or failed");
713
0
714
0
    gfxFontEntry* fe = nullptr;
715
0
716
0
    gfxUserFontType fontType =
717
0
        gfxFontUtils::DetermineFontDataType(aFontData, aLength);
718
0
    Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(fontType));
719
0
720
0
    // Unwrap/decompress/sanitize or otherwise munge the downloaded data
721
0
    // to make a usable sfnt structure.
722
0
723
0
    // Because platform font activation code may replace the name table
724
0
    // in the font with a synthetic one, we save the original name so that
725
0
    // it can be reported via the InspectorUtils API.
726
0
    nsAutoCString originalFullName;
727
0
728
0
    // Call the OTS sanitizer; this will also decode WOFF to sfnt
729
0
    // if necessary. The original data in aFontData is left unchanged.
730
0
    uint32_t saneLen;
731
0
    uint32_t fontCompressionRatio = 0;
732
0
    size_t computedSize = 0;
733
0
    const uint8_t* saneData =
734
0
        SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType);
735
0
    if (!saneData) {
736
0
        mFontSet->LogMessage(this, "rejected by sanitizer");
737
0
    } else {
738
0
        // Check whether saneData is a known OpenType format; it might be
739
0
        // a TrueType Collection, which OTS would accept but we don't yet
740
0
        // know how to handle. If so, discard.
741
0
        if (gfxFontUtils::DetermineFontDataType(saneData, saneLen) !=
742
0
            GFX_USERFONT_OPENTYPE) {
743
0
            mFontSet->LogMessage(this, "not a supported OpenType format");
744
0
            free((void*)saneData);
745
0
            saneData = nullptr;
746
0
        }
747
0
    }
748
0
    if (saneData) {
749
0
        if (saneLen) {
750
0
            fontCompressionRatio = uint32_t(100.0 * aLength / saneLen + 0.5);
751
0
            if (fontType == GFX_USERFONT_WOFF ||
752
0
                fontType == GFX_USERFONT_WOFF2) {
753
0
                Telemetry::Accumulate(fontType == GFX_USERFONT_WOFF ?
754
0
                                      Telemetry::WEBFONT_COMPRESSION_WOFF :
755
0
                                      Telemetry::WEBFONT_COMPRESSION_WOFF2,
756
0
                                      fontCompressionRatio);
757
0
                }
758
0
        }
759
0
760
0
        // The sanitizer ensures that we have a valid sfnt and a usable
761
0
        // name table, so this should never fail unless we're out of
762
0
        // memory, and GetFullNameFromSFNT is not directly exposed to
763
0
        // arbitrary/malicious data from the web.
764
0
        gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
765
0
                                          originalFullName);
766
0
767
0
        // Record size for memory reporting purposes. We measure this now
768
0
        // because by the time we potentially want to collect reports, this
769
0
        // data block may have been handed off to opaque OS font APIs that
770
0
        // don't allow us to retrieve or measure it directly.
771
0
        // The *OnAlloc function will also tell DMD about this block, as the
772
0
        // OS font code may hold on to it for an extended period.
773
0
        computedSize = UserFontMallocSizeOfOnAlloc(saneData);
774
0
775
0
        // Here ownership of saneData is passed to the platform,
776
0
        // which will delete it when no longer required
777
0
        fe = gfxPlatform::GetPlatform()->MakePlatformFont(mName,
778
0
                                                          Weight(),
779
0
                                                          Stretch(),
780
0
                                                          SlantStyle(),
781
0
                                                          saneData,
782
0
                                                          saneLen);
783
0
        if (!fe) {
784
0
            mFontSet->LogMessage(this, "not usable by platform");
785
0
        }
786
0
    }
787
0
788
0
    if (fe) {
789
0
        fe->mComputedSizeOfUserFont = computedSize;
790
0
791
0
        // Save a copy of the metadata block (if present) for InspectorUtils
792
0
        // to use if required. Ownership of the metadata block will be passed
793
0
        // to the gfxUserFontData record below.
794
0
        FallibleTArray<uint8_t> metadata;
795
0
        uint32_t metaOrigLen = 0;
796
0
        uint8_t compression = gfxUserFontData::kUnknownCompression;
797
0
        if (fontType == GFX_USERFONT_WOFF) {
798
0
            CopyWOFFMetadata<WOFFHeader>(aFontData, aLength,
799
0
                                         &metadata, &metaOrigLen);
800
0
            compression = gfxUserFontData::kZlibCompression;
801
0
        } else if (fontType == GFX_USERFONT_WOFF2) {
802
0
            CopyWOFFMetadata<WOFF2Header>(aFontData, aLength,
803
0
                                          &metadata, &metaOrigLen);
804
0
            compression = gfxUserFontData::kBrotliCompression;
805
0
        }
806
0
807
0
        // copy OpenType feature/language settings from the userfont entry to the
808
0
        // newly-created font entry
809
0
        fe->mFeatureSettings.AppendElements(mFeatureSettings);
810
0
        fe->mVariationSettings.AppendElements(mVariationSettings);
811
0
        fe->mLanguageOverride = mLanguageOverride;
812
0
        fe->mFamilyName = mFamilyName;
813
0
        fe->mRangeFlags = mRangeFlags;
814
0
        StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName,
815
0
                          &metadata, metaOrigLen, compression);
816
0
        if (LOG_ENABLED()) {
817
0
            LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
818
0
                 "(%p) gen: %8.8x compress: %d%%\n",
819
0
                 mFontSet, mSrcIndex,
820
0
                 mSrcList[mSrcIndex].mURI->GetSpecOrDefault().get(),
821
0
                 mFamilyName.get(),
822
0
                 this, uint32_t(mFontSet->mGeneration), fontCompressionRatio));
823
0
        }
824
0
        mPlatformFontEntry = fe;
825
0
        SetLoadState(STATUS_LOADED);
826
0
        gfxUserFontSet::UserFontCache::CacheFont(fe);
827
0
    } else {
828
0
        if (LOG_ENABLED()) {
829
0
            LOG(("userfonts (%p) [src %d] failed uri: (%s) for (%s)"
830
0
                 " error making platform font\n",
831
0
                 mFontSet, mSrcIndex,
832
0
                 mSrcList[mSrcIndex].mURI->GetSpecOrDefault().get(),
833
0
                 mFamilyName.get()));
834
0
        }
835
0
    }
836
0
837
0
    // The downloaded data can now be discarded; the font entry is using the
838
0
    // sanitized copy
839
0
    free((void*)aFontData);
840
0
841
0
    return fe != nullptr;
842
0
}
843
844
void
845
gfxUserFontEntry::Load()
846
0
{
847
0
    if (mUserFontLoadState == STATUS_NOT_LOADED) {
848
0
        LoadNextSrc();
849
0
    }
850
0
}
851
852
void
853
gfxUserFontEntry::IncrementGeneration()
854
0
{
855
0
    nsTArray<gfxUserFontSet*> fontSets;
856
0
    GetUserFontSets(fontSets);
857
0
    for (gfxUserFontSet* fontSet : fontSets) {
858
0
        fontSet->IncrementGeneration();
859
0
    }
860
0
}
861
862
// This is called when a font download finishes.
863
// Ownership of aFontData passes in here, and the font set must
864
// ensure that it is eventually deleted via free().
865
bool
866
gfxUserFontEntry::FontDataDownloadComplete(const uint8_t* aFontData,
867
                                           uint32_t aLength,
868
                                           nsresult aDownloadStatus)
869
0
{
870
0
    // forget about the loader, as we no longer potentially need to cancel it
871
0
    // if the entry is obsoleted
872
0
    mLoader = nullptr;
873
0
874
0
    // download successful, make platform font using font data
875
0
    if (NS_SUCCEEDED(aDownloadStatus) &&
876
0
        mFontDataLoadingState != LOADING_TIMED_OUT) {
877
0
        bool loaded = LoadPlatformFont(aFontData, aLength);
878
0
        aFontData = nullptr;
879
0
880
0
        if (loaded) {
881
0
            IncrementGeneration();
882
0
            return true;
883
0
        }
884
0
885
0
    } else {
886
0
        // download failed
887
0
        mFontSet->LogMessage(this,
888
0
                             (mFontDataLoadingState != LOADING_TIMED_OUT ?
889
0
                              "download failed" : "download timed out"),
890
0
                             nsIScriptError::errorFlag,
891
0
                             aDownloadStatus);
892
0
    }
893
0
894
0
    if (aFontData) {
895
0
        free((void*)aFontData);
896
0
    }
897
0
898
0
    // Error occurred.  Make sure the FontFace's promise is rejected if the
899
0
    // load timed out, or else load the next src.
900
0
    if (mFontDataLoadingState == LOADING_TIMED_OUT) {
901
0
      mFontDataLoadingState = LOADING_FAILED;
902
0
      SetLoadState(STATUS_FAILED);
903
0
    } else {
904
0
      LoadNextSrc();
905
0
    }
906
0
907
0
    // We ignore the status returned by LoadNext();
908
0
    // even if loading failed, we need to bump the font-set generation
909
0
    // and return true in order to trigger reflow, so that fallback
910
0
    // will be used where the text was "masked" by the pending download
911
0
    IncrementGeneration();
912
0
    return true;
913
0
}
914
915
void
916
gfxUserFontEntry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult)
917
0
{
918
0
    aResult.Clear();
919
0
    aResult.AppendElement(mFontSet);
920
0
}
921
922
gfxUserFontSet::gfxUserFontSet()
923
    : mFontFamilies(4),
924
      mRebuildGeneration(0),
925
      mLocalRulesUsed(false),
926
      mRebuildLocalRules(false),
927
      mDownloadCount(0),
928
      mDownloadSize(0)
929
0
{
930
0
    IncrementGeneration(true);
931
0
    gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
932
0
    if (fp) {
933
0
        fp->AddUserFontSet(this);
934
0
    }
935
0
}
936
937
gfxUserFontSet::~gfxUserFontSet()
938
0
{
939
0
    gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
940
0
    if (fp) {
941
0
        fp->RemoveUserFontSet(this);
942
0
    }
943
0
}
944
945
already_AddRefed<gfxUserFontEntry>
946
gfxUserFontSet::FindOrCreateUserFontEntry(
947
                               const nsACString& aFamilyName,
948
                               const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
949
                               WeightRange aWeight,
950
                               StretchRange aStretch,
951
                               SlantStyleRange aStyle,
952
                               const nsTArray<gfxFontFeature>& aFeatureSettings,
953
                               const nsTArray<gfxFontVariation>& aVariationSettings,
954
                               uint32_t aLanguageOverride,
955
                               gfxCharacterMap* aUnicodeRanges,
956
                               uint8_t aFontDisplay,
957
                               RangeFlags aRangeFlags)
958
0
{
959
0
    RefPtr<gfxUserFontEntry> entry;
960
0
961
0
    // If there's already a userfont entry in the family whose descriptors all match,
962
0
    // we can just move it to the end of the list instead of adding a new
963
0
    // face that will always "shadow" the old one.
964
0
    // Note that we can't do this for platform font entries, even if the
965
0
    // style descriptors match, as they might have had a different source list,
966
0
    // but we no longer have the old source list available to check.
967
0
    gfxUserFontFamily* family = LookupFamily(aFamilyName);
968
0
    if (family) {
969
0
        entry = FindExistingUserFontEntry(family, aFontFaceSrcList, aWeight,
970
0
                                          aStretch, aStyle,
971
0
                                          aFeatureSettings, aVariationSettings,
972
0
                                          aLanguageOverride,
973
0
                                          aUnicodeRanges, aFontDisplay,
974
0
                                          aRangeFlags);
975
0
    }
976
0
977
0
    if (!entry) {
978
0
      entry = CreateUserFontEntry(aFontFaceSrcList, aWeight, aStretch,
979
0
                                  aStyle, aFeatureSettings, aVariationSettings,
980
0
                                  aLanguageOverride, aUnicodeRanges,
981
0
                                  aFontDisplay, aRangeFlags);
982
0
      entry->mFamilyName = aFamilyName;
983
0
    }
984
0
985
0
    return entry.forget();
986
0
}
987
988
gfxUserFontEntry*
989
gfxUserFontSet::FindExistingUserFontEntry(
990
                               gfxUserFontFamily* aFamily,
991
                               const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
992
                               WeightRange aWeight,
993
                               StretchRange aStretch,
994
                               SlantStyleRange aStyle,
995
                               const nsTArray<gfxFontFeature>& aFeatureSettings,
996
                               const nsTArray<gfxFontVariation>& aVariationSettings,
997
                               uint32_t aLanguageOverride,
998
                               gfxCharacterMap* aUnicodeRanges,
999
                               uint8_t aFontDisplay,
1000
                               RangeFlags aRangeFlags)
1001
0
{
1002
0
    nsTArray<RefPtr<gfxFontEntry>>& fontList = aFamily->GetFontList();
1003
0
1004
0
    for (size_t i = 0, count = fontList.Length(); i < count; i++) {
1005
0
        if (!fontList[i]->mIsUserFontContainer) {
1006
0
            continue;
1007
0
        }
1008
0
1009
0
        gfxUserFontEntry* existingUserFontEntry =
1010
0
            static_cast<gfxUserFontEntry*>(fontList[i].get());
1011
0
        if (!existingUserFontEntry->Matches(aFontFaceSrcList,
1012
0
                                            aWeight, aStretch, aStyle,
1013
0
                                            aFeatureSettings, aVariationSettings,
1014
0
                                            aLanguageOverride,
1015
0
                                            aUnicodeRanges, aFontDisplay,
1016
0
                                            aRangeFlags)) {
1017
0
            continue;
1018
0
        }
1019
0
1020
0
        return existingUserFontEntry;
1021
0
    }
1022
0
1023
0
    return nullptr;
1024
0
}
1025
1026
void
1027
gfxUserFontSet::AddUserFontEntry(const nsCString& aFamilyName,
1028
                                 gfxUserFontEntry* aUserFontEntry)
1029
0
{
1030
0
    gfxUserFontFamily* family = GetFamily(aFamilyName);
1031
0
    family->AddFontEntry(aUserFontEntry);
1032
0
1033
0
    if (LOG_ENABLED()) {
1034
0
        nsAutoCString weightString;
1035
0
        aUserFontEntry->Weight().ToString(weightString);
1036
0
        nsAutoCString stretchString;
1037
0
        aUserFontEntry->Stretch().ToString(stretchString);
1038
0
        LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
1039
0
             "stretch: %s display: %d",
1040
0
             this, aFamilyName.get(), aUserFontEntry,
1041
0
             (aUserFontEntry->IsItalic() ? "italic" :
1042
0
              (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
1043
0
             weightString.get(),
1044
0
             stretchString.get(),
1045
0
             aUserFontEntry->GetFontDisplay()));
1046
0
    }
1047
0
}
1048
1049
void
1050
gfxUserFontSet::IncrementGeneration(bool aIsRebuild)
1051
0
{
1052
0
    // add one, increment again if zero
1053
0
    ++sFontSetGeneration;
1054
0
    if (sFontSetGeneration == 0)
1055
0
       ++sFontSetGeneration;
1056
0
    mGeneration = sFontSetGeneration;
1057
0
    if (aIsRebuild) {
1058
0
        mRebuildGeneration = mGeneration;
1059
0
    }
1060
0
}
1061
1062
void
1063
gfxUserFontSet::RebuildLocalRules()
1064
0
{
1065
0
    if (mLocalRulesUsed) {
1066
0
        mRebuildLocalRules = true;
1067
0
        DoRebuildUserFontSet();
1068
0
    }
1069
0
}
1070
1071
gfxUserFontFamily*
1072
gfxUserFontSet::LookupFamily(const nsACString& aFamilyName) const
1073
0
{
1074
0
    nsAutoCString key(aFamilyName);
1075
0
    ToLowerCase(key);
1076
0
1077
0
    return mFontFamilies.GetWeak(key);
1078
0
}
1079
1080
bool
1081
gfxUserFontSet::ContainsUserFontSetFonts(const FontFamilyList& aFontList) const
1082
0
{
1083
0
    for (const FontFamilyName& name : aFontList.GetFontlist()->mNames) {
1084
0
        if (!name.IsNamed()) {
1085
0
            continue;
1086
0
        }
1087
0
        if (LookupFamily(nsAtomCString(name.mName))) {
1088
0
            return true;
1089
0
        }
1090
0
    }
1091
0
    return false;
1092
0
}
1093
1094
gfxUserFontFamily*
1095
gfxUserFontSet::GetFamily(const nsACString& aFamilyName)
1096
0
{
1097
0
    nsAutoCString key(aFamilyName);
1098
0
    ToLowerCase(key);
1099
0
1100
0
    gfxUserFontFamily* family = mFontFamilies.GetWeak(key);
1101
0
    if (!family) {
1102
0
        family = new gfxUserFontFamily(aFamilyName);
1103
0
        mFontFamilies.Put(key, family);
1104
0
    }
1105
0
    return family;
1106
0
}
1107
1108
///////////////////////////////////////////////////////////////////////////////
1109
// gfxUserFontSet::UserFontCache - re-use platform font entries for user fonts
1110
// across pages/fontsets rather than instantiating new platform fonts.
1111
//
1112
// Entries are added to this cache when a platform font is instantiated from
1113
// downloaded data, and removed when the platform font entry is destroyed.
1114
// We don't need to use a timed expiration scheme here because the gfxFontEntry
1115
// for a downloaded font will be kept alive by its corresponding gfxFont
1116
// instance(s) until they are deleted, and *that* happens using an expiration
1117
// tracker (gfxFontCache). The result is that the downloaded font instances
1118
// recorded here will persist between pages and can get reused (provided the
1119
// source URI and principal match, of course).
1120
///////////////////////////////////////////////////////////////////////////////
1121
1122
nsTHashtable<gfxUserFontSet::UserFontCache::Entry>*
1123
gfxUserFontSet::UserFontCache::sUserFonts = nullptr;
1124
1125
NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::Flusher, nsIObserver)
1126
1127
NS_IMETHODIMP
1128
gfxUserFontSet::UserFontCache::Flusher::Observe(nsISupports* aSubject,
1129
                                                const char* aTopic,
1130
                                                const char16_t* aData)
1131
0
{
1132
0
    if (!sUserFonts) {
1133
0
        return NS_OK;
1134
0
    }
1135
0
1136
0
    if (!strcmp(aTopic, "cacheservice:empty-cache")) {
1137
0
        for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1138
0
            i.Remove();
1139
0
        }
1140
0
    } else if (!strcmp(aTopic, "last-pb-context-exited")) {
1141
0
        for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1142
0
            if (i.Get()->IsPrivate()) {
1143
0
                i.Remove();
1144
0
            }
1145
0
        }
1146
0
    } else if (!strcmp(aTopic, "xpcom-shutdown")) {
1147
0
        for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1148
0
            i.Get()->GetFontEntry()->DisconnectSVG();
1149
0
        }
1150
0
    } else {
1151
0
        MOZ_ASSERT_UNREACHABLE("unexpected topic");
1152
0
    }
1153
0
1154
0
    return NS_OK;
1155
0
}
1156
1157
bool
1158
gfxUserFontSet::UserFontCache::Entry::KeyEquals(const KeyTypePointer aKey) const
1159
0
{
1160
0
    const gfxFontEntry* fe = aKey->mFontEntry;
1161
0
1162
0
    if (!mURI->Equals(aKey->mURI)) {
1163
0
        return false;
1164
0
    }
1165
0
1166
0
    // For data: URIs, we don't care about the principal; otherwise, check it.
1167
0
    if (!IgnorePrincipal(mURI)) {
1168
0
        NS_ASSERTION(mPrincipal && aKey->mPrincipal,
1169
0
                     "only data: URIs are allowed to omit the principal");
1170
0
        if (!mPrincipal->Equals(aKey->mPrincipal)) {
1171
0
            return false;
1172
0
        }
1173
0
    }
1174
0
1175
0
    if (mPrivate != aKey->mPrivate) {
1176
0
        return false;
1177
0
    }
1178
0
1179
0
    if (mFontEntry->SlantStyle()      != fe->SlantStyle()     ||
1180
0
        mFontEntry->Weight()          != fe->Weight()         ||
1181
0
        mFontEntry->Stretch()         != fe->Stretch()        ||
1182
0
        mFontEntry->mFeatureSettings  != fe->mFeatureSettings ||
1183
0
        mFontEntry->mVariationSettings != fe->mVariationSettings ||
1184
0
        mFontEntry->mLanguageOverride != fe->mLanguageOverride ||
1185
0
        mFontEntry->mFamilyName       != fe->mFamilyName) {
1186
0
        return false;
1187
0
    }
1188
0
1189
0
    return true;
1190
0
}
1191
1192
void
1193
gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry)
1194
0
{
1195
0
    NS_ASSERTION(aFontEntry->mFamilyName.Length() != 0,
1196
0
                 "caching a font associated with no family yet");
1197
0
1198
0
    // if caching is disabled, simply return
1199
0
    if (Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1200
0
        return;
1201
0
    }
1202
0
1203
0
    gfxUserFontData* data = aFontEntry->mUserFontData.get();
1204
0
    if (data->mIsBuffer) {
1205
#ifdef DEBUG_USERFONT_CACHE
1206
        printf("userfontcache skipped fontentry with buffer source: %p\n",
1207
               aFontEntry);
1208
#endif
1209
        return;
1210
0
    }
1211
0
1212
0
    if (!sUserFonts) {
1213
0
        sUserFonts = new nsTHashtable<Entry>;
1214
0
1215
0
        nsCOMPtr<nsIObserverService> obs =
1216
0
            mozilla::services::GetObserverService();
1217
0
        if (obs) {
1218
0
            Flusher* flusher = new Flusher;
1219
0
            obs->AddObserver(flusher, "cacheservice:empty-cache",
1220
0
                             false);
1221
0
            obs->AddObserver(flusher, "last-pb-context-exited", false);
1222
0
            obs->AddObserver(flusher, "xpcom-shutdown", false);
1223
0
        }
1224
0
1225
0
        // Create and register a memory reporter for sUserFonts.
1226
0
        // This reporter is never unregistered, but that's OK because
1227
0
        // the reporter checks whether sUserFonts is null, so it would
1228
0
        // be safe to call even after UserFontCache::Shutdown has deleted
1229
0
        // the cache.
1230
0
        RegisterStrongMemoryReporter(new MemoryReporter());
1231
0
    }
1232
0
1233
0
    // For data: URIs, the principal is ignored; anyone who has the same
1234
0
    // data: URI is able to load it and get an equivalent font.
1235
0
    // Otherwise, the principal is used as part of the cache key.
1236
0
    gfxFontSrcPrincipal* principal;
1237
0
    if (IgnorePrincipal(data->mURI)) {
1238
0
        principal = nullptr;
1239
0
    } else {
1240
0
        principal = data->mPrincipal;
1241
0
    }
1242
0
    sUserFonts->PutEntry(Key(data->mURI, principal, aFontEntry,
1243
0
                             data->mPrivate));
1244
0
1245
#ifdef DEBUG_USERFONT_CACHE
1246
    printf("userfontcache added fontentry: %p\n", aFontEntry);
1247
    Dump();
1248
#endif
1249
}
1250
1251
void
1252
gfxUserFontSet::UserFontCache::ForgetFont(gfxFontEntry* aFontEntry)
1253
0
{
1254
0
    if (!sUserFonts) {
1255
0
        // if we've already deleted the cache (i.e. during shutdown),
1256
0
        // just ignore this
1257
0
        return;
1258
0
    }
1259
0
1260
0
    // We can't simply use RemoveEntry here because it's possible the principal
1261
0
    // may have changed since the font was cached, in which case the lookup
1262
0
    // would no longer find the entry (bug 838105).
1263
0
    for (auto i = sUserFonts->Iter(); !i.Done(); i.Next()) {
1264
0
        if (i.Get()->GetFontEntry() == aFontEntry)  {
1265
0
            i.Remove();
1266
0
        }
1267
0
    }
1268
0
1269
#ifdef DEBUG_USERFONT_CACHE
1270
    printf("userfontcache removed fontentry: %p\n", aFontEntry);
1271
    Dump();
1272
#endif
1273
}
1274
1275
gfxFontEntry*
1276
gfxUserFontSet::UserFontCache::GetFont(const gfxFontFaceSrc& aSrc,
1277
                                       const gfxUserFontEntry& aUserFontEntry)
1278
0
{
1279
0
    if (!sUserFonts ||
1280
0
        aUserFontEntry.mFontSet->BypassCache() ||
1281
0
        Preferences::GetBool("gfx.downloadable_fonts.disable_cache")) {
1282
0
        return nullptr;
1283
0
    }
1284
0
1285
0
    // Ignore principal when looking up a data: URI.
1286
0
    gfxFontSrcPrincipal* principal = IgnorePrincipal(aSrc.mURI)
1287
0
      ? nullptr
1288
0
      : aSrc.LoadPrincipal(*aUserFontEntry.mFontSet);
1289
0
1290
0
    Entry* entry = sUserFonts->GetEntry(
1291
0
      Key(aSrc.mURI,
1292
0
          principal,
1293
0
          const_cast<gfxUserFontEntry*>(&aUserFontEntry),
1294
0
          aUserFontEntry.mFontSet->GetPrivateBrowsing()));
1295
0
    if (!entry) {
1296
0
        return nullptr;
1297
0
    }
1298
0
1299
0
    // We have to perform another content policy check here to prevent
1300
0
    // cache poisoning. E.g. a.com loads a font into the cache but
1301
0
    // b.com has a CSP not allowing any fonts to be loaded.
1302
0
    if (!aUserFontEntry.mFontSet->IsFontLoadAllowed(aSrc)) {
1303
0
        return nullptr;
1304
0
    }
1305
0
1306
0
    return entry->GetFontEntry();
1307
0
}
1308
1309
void
1310
gfxUserFontSet::UserFontCache::Shutdown()
1311
0
{
1312
0
    if (sUserFonts) {
1313
0
        delete sUserFonts;
1314
0
        sUserFonts = nullptr;
1315
0
    }
1316
0
}
1317
1318
MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
1319
1320
void
1321
gfxUserFontSet::UserFontCache::Entry::ReportMemory(
1322
    nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
1323
0
{
1324
0
    MOZ_ASSERT(mFontEntry);
1325
0
    nsAutoCString path("explicit/gfx/user-fonts/font(");
1326
0
1327
0
    if (aAnonymize) {
1328
0
        path.AppendPrintf("<anonymized-%p>", this);
1329
0
    } else {
1330
0
        path.AppendPrintf("family=%s", mFontEntry->mFamilyName.get());
1331
0
        if (mURI) {
1332
0
            nsCString spec = mURI->GetSpecOrDefault();
1333
0
            spec.ReplaceChar('/', '\\');
1334
0
            // Some fonts are loaded using horrendously-long data: URIs;
1335
0
            // truncate those before reporting them.
1336
0
            bool isData;
1337
0
            if (NS_SUCCEEDED(mURI->get()->SchemeIs("data", &isData)) && isData &&
1338
0
                spec.Length() > 255) {
1339
0
                spec.Truncate(252);
1340
0
                spec.AppendLiteral("...");
1341
0
            }
1342
0
            path.AppendPrintf(", url=%s", spec.get());
1343
0
        }
1344
0
        if (mPrincipal) {
1345
0
            nsCOMPtr<nsIURI> uri;
1346
0
            mPrincipal->get()->GetURI(getter_AddRefs(uri));
1347
0
            if (uri) {
1348
0
                nsCString spec = uri->GetSpecOrDefault();
1349
0
                if (!spec.IsEmpty()) {
1350
0
                    // Include a clue as to who loaded this resource. (Note
1351
0
                    // that because of font entry sharing, other pages may now
1352
0
                    // be using this resource, and the original page may not
1353
0
                    // even be loaded any longer.)
1354
0
                    spec.ReplaceChar('/', '\\');
1355
0
                    path.AppendPrintf(", principal=%s", spec.get());
1356
0
                }
1357
0
            }
1358
0
        }
1359
0
    }
1360
0
    path.Append(')');
1361
0
1362
0
    aHandleReport->Callback(
1363
0
        EmptyCString(), path,
1364
0
        nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
1365
0
        mFontEntry->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf),
1366
0
        NS_LITERAL_CSTRING("Memory used by @font-face resource."),
1367
0
        aData);
1368
0
}
1369
1370
NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter,
1371
                  nsIMemoryReporter)
1372
1373
NS_IMETHODIMP
1374
gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
1375
    nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
1376
0
{
1377
0
    if (!sUserFonts) {
1378
0
        return NS_OK;
1379
0
    }
1380
0
1381
0
    for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1382
0
        it.Get()->ReportMemory(aHandleReport, aData, aAnonymize);
1383
0
    }
1384
0
1385
0
    MOZ_COLLECT_REPORT(
1386
0
        "explicit/gfx/user-fonts/cache-overhead", KIND_HEAP, UNITS_BYTES,
1387
0
        sUserFonts->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf),
1388
0
        "Memory used by the @font-face cache, not counting the actual font "
1389
0
        "resources.");
1390
0
1391
0
    return NS_OK;
1392
0
}
1393
1394
#ifdef DEBUG_USERFONT_CACHE
1395
1396
void
1397
gfxUserFontSet::UserFontCache::Entry::Dump()
1398
{
1399
    nsresult rv;
1400
1401
    nsAutoCString principalURISpec("(null)");
1402
    bool setDomain = false;
1403
1404
    if (mPrincipal) {
1405
        nsCOMPtr<nsIURI> principalURI;
1406
        rv = mPrincipal->get()->GetURI(getter_AddRefs(principalURI));
1407
        if (NS_SUCCEEDED(rv)) {
1408
            principalURI->GetSpec(principalURISpec);
1409
        }
1410
1411
        nsCOMPtr<nsIURI> domainURI;
1412
        mPrincipal->get()->GetDomain(getter_AddRefs(domainURI));
1413
        if (domainURI) {
1414
            setDomain = true;
1415
        }
1416
    }
1417
1418
    NS_ASSERTION(mURI, "null URI in userfont cache entry");
1419
1420
    printf("userfontcache fontEntry: %p fonturihash: %8.8x "
1421
           "family: %s domainset: %s principal: [%s]\n",
1422
           mFontEntry,
1423
           mURI->Hash(),
1424
           mFontEntry->FamilyName().get(),
1425
           setDomain ? "true" : "false",
1426
           principalURISpec.get());
1427
}
1428
1429
void
1430
gfxUserFontSet::UserFontCache::Dump()
1431
{
1432
    if (!sUserFonts) {
1433
        return;
1434
    }
1435
1436
    printf("userfontcache dump count: %d ========\n", sUserFonts->Count());
1437
    for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
1438
        it.Get()->Dump();
1439
    }
1440
    printf("userfontcache dump ==================\n");
1441
}
1442
1443
#endif
1444
1445
#undef LOG
1446
#undef LOG_ENABLED