tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxUserFontSet.cpp (52983B)


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