tor-browser

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

FontFaceSetImpl.cpp (30506B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FontFaceSetImpl.h"
      8 
      9 #include "ReferrerInfo.h"
     10 #include "gfxFontConstants.h"
     11 #include "gfxFontSrcPrincipal.h"
     12 #include "gfxFontSrcURI.h"
     13 #include "gfxFontUtils.h"
     14 #include "gfxPlatformFontList.h"
     15 #include "mozilla/AsyncEventDispatcher.h"
     16 #include "mozilla/BasePrincipal.h"
     17 #include "mozilla/FontPropertyTypes.h"
     18 #include "mozilla/LoadInfo.h"
     19 #include "mozilla/Logging.h"
     20 #include "mozilla/Preferences.h"
     21 #include "mozilla/PresShell.h"
     22 #include "mozilla/PresShellInlines.h"
     23 #include "mozilla/ServoBindings.h"
     24 #include "mozilla/ServoCSSParser.h"
     25 #include "mozilla/ServoStyleSet.h"
     26 #include "mozilla/ServoUtils.h"
     27 #include "mozilla/Sprintf.h"
     28 #include "mozilla/StaticPrefs_layout.h"
     29 #include "mozilla/css/Loader.h"
     30 #include "mozilla/dom/CSSFontFaceRule.h"
     31 #include "mozilla/dom/DocumentInlines.h"
     32 #include "mozilla/dom/Event.h"
     33 #include "mozilla/dom/FontFaceImpl.h"
     34 #include "mozilla/dom/FontFaceSet.h"
     35 #include "mozilla/dom/FontFaceSetBinding.h"
     36 #include "mozilla/dom/FontFaceSetLoadEvent.h"
     37 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
     38 #include "mozilla/dom/Promise.h"
     39 #include "mozilla/dom/WorkerCommon.h"
     40 #include "mozilla/dom/WorkerRunnable.h"
     41 #include "mozilla/glean/GfxMetrics.h"
     42 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     43 #include "nsComponentManagerUtils.h"
     44 #include "nsContentUtils.h"
     45 #include "nsDOMNavigationTiming.h"
     46 #include "nsDeviceContext.h"
     47 #include "nsFontFaceLoader.h"
     48 #include "nsIConsoleService.h"
     49 #include "nsIContentPolicy.h"
     50 #include "nsIDocShell.h"
     51 #include "nsIInputStream.h"
     52 #include "nsILoadContext.h"
     53 #include "nsIPrincipal.h"
     54 #include "nsIWebNavigation.h"
     55 #include "nsLayoutUtils.h"
     56 #include "nsNetUtil.h"
     57 #include "nsPresContext.h"
     58 #include "nsPrintfCString.h"
     59 #include "nsUTF8Utils.h"
     60 
     61 using namespace mozilla;
     62 using namespace mozilla::css;
     63 using namespace mozilla::dom;
     64 
     65 #define LOG(args) \
     66  MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
     67 #define LOG_ENABLED() \
     68  MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
     69 
     70 NS_IMPL_ISUPPORTS0(FontFaceSetImpl)
     71 
     72 FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner)
     73    : mOwner(aOwner),
     74      mStatus(FontFaceSetLoadStatus::Loaded),
     75      mNonRuleFacesDirty(false),
     76      mHasLoadingFontFaces(false),
     77      mHasLoadingFontFacesIsDirty(false),
     78      mDelayedLoadCheck(false),
     79      mBypassCache(false),
     80      mPrivateBrowsing(false) {}
     81 
     82 FontFaceSetImpl::~FontFaceSetImpl() {
     83  // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
     84  // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
     85  MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
     86 
     87  Destroy();
     88 }
     89 
     90 void FontFaceSetImpl::DestroyLoaders() {
     91  mMutex.AssertCurrentThreadIn();
     92  if (mLoaders.IsEmpty()) {
     93    return;
     94  }
     95  if (NS_IsMainThread()) {
     96    for (const auto& key : mLoaders.Keys()) {
     97      key->Cancel();
     98    }
     99    mLoaders.Clear();
    100    return;
    101  }
    102 
    103  class DestroyLoadersRunnable final : public Runnable {
    104   public:
    105    explicit DestroyLoadersRunnable(FontFaceSetImpl* aFontFaceSet)
    106        : Runnable("FontFaceSetImpl::DestroyLoaders"),
    107          mFontFaceSet(aFontFaceSet) {}
    108 
    109   protected:
    110    ~DestroyLoadersRunnable() override = default;
    111 
    112    NS_IMETHOD Run() override {
    113      RecursiveMutexAutoLock lock(mFontFaceSet->mMutex);
    114      mFontFaceSet->DestroyLoaders();
    115      return NS_OK;
    116    }
    117 
    118    // We need to save a reference to the FontFaceSetImpl because the
    119    // loaders contain a non-owning reference to it.
    120    RefPtr<FontFaceSetImpl> mFontFaceSet;
    121  };
    122 
    123  auto runnable = MakeRefPtr<DestroyLoadersRunnable>(this);
    124  NS_DispatchToMainThread(runnable);
    125 }
    126 
    127 void FontFaceSetImpl::Destroy() {
    128  nsTArray<FontFaceRecord> nonRuleFaces;
    129  nsRefPtrHashtable<nsCStringHashKey, gfxUserFontFamily> fontFamilies;
    130 
    131  {
    132    RecursiveMutexAutoLock lock(mMutex);
    133    DestroyLoaders();
    134    nonRuleFaces = std::move(mNonRuleFaces);
    135    fontFamilies = std::move(mFontFamilies);
    136    mOwner = nullptr;
    137  }
    138 
    139  if (gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList()) {
    140    fp->RemoveUserFontSet(this);
    141  }
    142 }
    143 
    144 void FontFaceSetImpl::ParseFontShorthandForMatching(
    145    const nsACString& aFont, StyleFontFamilyList& aFamilyList,
    146    FontWeight& aWeight, FontStretch& aStretch, FontSlantStyle& aStyle,
    147    ErrorResult& aRv) {
    148  RefPtr<URLExtraData> url = GetURLExtraData();
    149  if (!url) {
    150    aRv.ThrowInvalidStateError("Missing URLExtraData");
    151    return;
    152  }
    153 
    154  if (!ServoCSSParser::ParseFontShorthandForMatching(
    155          aFont, url, aFamilyList, aStyle, aStretch, aWeight)) {
    156    aRv.ThrowSyntaxError("Invalid font shorthand");
    157    return;
    158  }
    159 }
    160 
    161 static bool HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry,
    162                                          const nsAString& aInput) {
    163  const char16_t* p = aInput.Data();
    164  const char16_t* end = p + aInput.Length();
    165 
    166  while (p < end) {
    167    uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
    168    if (aEntry->CharacterInUnicodeRange(c)) {
    169      return true;
    170    }
    171  }
    172  return false;
    173 }
    174 
    175 void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont,
    176                                            const nsAString& aText,
    177                                            nsTArray<FontFace*>& aFontFaces,
    178                                            ErrorResult& aRv) {
    179  RecursiveMutexAutoLock lock(mMutex);
    180 
    181  StyleFontFamilyList familyList;
    182  FontWeight weight;
    183  FontStretch stretch;
    184  FontSlantStyle italicStyle;
    185  ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
    186                                aRv);
    187  if (aRv.Failed()) {
    188    return;
    189  }
    190 
    191  gfxFontStyle style;
    192  style.style = italicStyle;
    193  style.weight = weight;
    194  style.stretch = stretch;
    195 
    196  // Set of FontFaces that we want to return.
    197  nsTHashSet<FontFace*> matchingFaces;
    198 
    199  for (const StyleSingleFontFamily& fontFamilyName : familyList.list.AsSpan()) {
    200    if (!fontFamilyName.IsFamilyName()) {
    201      continue;
    202    }
    203 
    204    const auto& name = fontFamilyName.AsFamilyName();
    205    RefPtr<gfxFontFamily> family =
    206        LookupFamily(nsAtomCString(name.name.AsAtom()));
    207 
    208    if (!family) {
    209      continue;
    210    }
    211 
    212    AutoTArray<gfxFontEntry*, 4> entries;
    213    family->FindAllFontsForStyle(style, entries);
    214 
    215    for (gfxFontEntry* e : entries) {
    216      FontFaceImpl::Entry* entry = static_cast<FontFaceImpl::Entry*>(e);
    217      if (HasAnyCharacterInUnicodeRange(entry, aText)) {
    218        entry->FindFontFaceOwners(matchingFaces);
    219      }
    220    }
    221  }
    222 
    223  if (matchingFaces.IsEmpty()) {
    224    return;
    225  }
    226 
    227  // Add all FontFaces in matchingFaces to aFontFaces, in the order
    228  // they appear in the FontFaceSet.
    229  FindMatchingFontFaces(matchingFaces, aFontFaces);
    230 }
    231 
    232 void FontFaceSetImpl::FindMatchingFontFaces(
    233    const nsTHashSet<FontFace*>& aMatchingFaces,
    234    nsTArray<FontFace*>& aFontFaces) {
    235  RecursiveMutexAutoLock lock(mMutex);
    236  for (FontFaceRecord& record : mNonRuleFaces) {
    237    FontFace* owner = record.mFontFace->GetOwner();
    238    if (owner && aMatchingFaces.Contains(owner)) {
    239      aFontFaces.AppendElement(owner);
    240    }
    241  }
    242 }
    243 
    244 bool FontFaceSetImpl::ReadyPromiseIsPending() const {
    245  RecursiveMutexAutoLock lock(mMutex);
    246  return mOwner && mOwner->ReadyPromiseIsPending();
    247 }
    248 
    249 FontFaceSetLoadStatus FontFaceSetImpl::Status() {
    250  RecursiveMutexAutoLock lock(mMutex);
    251  FlushUserFontSet();
    252  return mStatus;
    253 }
    254 
    255 bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
    256  RecursiveMutexAutoLock lock(mMutex);
    257  if (aFontFace->IsInFontFaceSet(this)) {
    258    return false;
    259  }
    260 
    261  if (aFontFace->HasRule()) {
    262    aRv.ThrowInvalidModificationError(
    263        "Can't add face to FontFaceSet that comes from an @font-face rule");
    264    return false;
    265  }
    266 
    267  aFontFace->AddFontFaceSet(this);
    268 
    269 #ifdef DEBUG
    270  for (const FontFaceRecord& rec : mNonRuleFaces) {
    271    MOZ_ASSERT(rec.mFontFace != aFontFace,
    272               "FontFace should not occur in mNonRuleFaces twice");
    273  }
    274 #endif
    275 
    276  FontFaceRecord* rec = mNonRuleFaces.AppendElement();
    277  rec->mFontFace = aFontFace;
    278  rec->mOrigin = Nothing();
    279 
    280  mNonRuleFacesDirty = true;
    281  MarkUserFontSetDirty();
    282  mHasLoadingFontFacesIsDirty = true;
    283  CheckLoadingStarted();
    284  return true;
    285 }
    286 
    287 void FontFaceSetImpl::Clear() {
    288  RecursiveMutexAutoLock lock(mMutex);
    289  if (mNonRuleFaces.IsEmpty()) {
    290    return;
    291  }
    292 
    293  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
    294    FontFaceImpl* f = mNonRuleFaces[i].mFontFace;
    295    f->RemoveFontFaceSet(this);
    296  }
    297 
    298  mNonRuleFaces.Clear();
    299  mNonRuleFacesDirty = true;
    300  MarkUserFontSetDirty();
    301  mHasLoadingFontFacesIsDirty = true;
    302  CheckLoadingFinished();
    303 }
    304 
    305 bool FontFaceSetImpl::Delete(FontFaceImpl* aFontFace) {
    306  RecursiveMutexAutoLock lock(mMutex);
    307  if (aFontFace->HasRule()) {
    308    return false;
    309  }
    310 
    311  bool removed = false;
    312  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
    313    if (mNonRuleFaces[i].mFontFace == aFontFace) {
    314      mNonRuleFaces.RemoveElementAt(i);
    315      removed = true;
    316      break;
    317    }
    318  }
    319  if (!removed) {
    320    return false;
    321  }
    322 
    323  aFontFace->RemoveFontFaceSet(this);
    324 
    325  mNonRuleFacesDirty = true;
    326  MarkUserFontSetDirty();
    327  mHasLoadingFontFacesIsDirty = true;
    328  CheckLoadingFinished();
    329  return true;
    330 }
    331 
    332 bool FontFaceSetImpl::HasAvailableFontFace(FontFaceImpl* aFontFace) {
    333  return aFontFace->IsInFontFaceSet(this);
    334 }
    335 
    336 void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader* aLoader) {
    337  RecursiveMutexAutoLock lock(mMutex);
    338  mLoaders.RemoveEntry(aLoader);
    339 }
    340 
    341 void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace) {
    342  gfxUserFontAttributes attr;
    343  if (!aFontFace->GetAttributes(attr)) {
    344    // If there is no family name, this rule cannot contribute a
    345    // usable font, so there is no point in processing it further.
    346    return;
    347  }
    348 
    349  nsAutoCString family(attr.mFamilyName);
    350 
    351  // Just create a new font entry if we haven't got one already.
    352  if (!aFontFace->GetUserFontEntry()) {
    353    // XXX Should we be checking mLocalRulesUsed like InsertRuleFontFace does?
    354    RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace(
    355        aFontFace, std::move(attr), StyleOrigin::Author);
    356    if (!entry) {
    357      return;
    358    }
    359    aFontFace->SetUserFontEntry(entry);
    360  }
    361  AddUserFontEntry(family, aFontFace->GetUserFontEntry());
    362 }
    363 
    364 void FontFaceSetImpl::UpdateUserFontEntry(gfxUserFontEntry* aEntry,
    365                                          gfxUserFontAttributes&& aAttr) {
    366  MOZ_ASSERT(NS_IsMainThread());
    367 
    368  bool resetFamilyName = !aEntry->mFamilyName.IsEmpty() &&
    369                         aEntry->mFamilyName != aAttr.mFamilyName;
    370  // aFontFace already has a user font entry, so we update its attributes
    371  // rather than creating a new one.
    372  aEntry->UpdateAttributes(std::move(aAttr));
    373  // If the family name has changed, remove the entry from its current family
    374  // and clear the mFamilyName field so it can be reset when added to a new
    375  // family.
    376  if (resetFamilyName) {
    377    RefPtr<gfxUserFontFamily> family = LookupFamily(aEntry->mFamilyName);
    378    if (family) {
    379      family->RemoveFontEntry(aEntry);
    380    }
    381    aEntry->mFamilyName.Truncate(0);
    382  }
    383 }
    384 
    385 class FontFaceSetImpl::UpdateUserFontEntryRunnable final
    386    : public WorkerMainThreadRunnable {
    387 public:
    388  UpdateUserFontEntryRunnable(FontFaceSetImpl* aSet, gfxUserFontEntry* aEntry,
    389                              gfxUserFontAttributes& aAttr)
    390      : WorkerMainThreadRunnable(
    391            GetCurrentThreadWorkerPrivate(),
    392            "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns),
    393        mSet(aSet),
    394        mEntry(aEntry),
    395        mAttr(aAttr) {}
    396 
    397  bool MainThreadRun() override {
    398    mSet->UpdateUserFontEntry(mEntry, std::move(mAttr));
    399    return true;
    400  }
    401 
    402 private:
    403  FontFaceSetImpl* mSet;
    404  gfxUserFontEntry* mEntry;
    405  gfxUserFontAttributes& mAttr;
    406 };
    407 
    408 // TODO(emilio): Should this take an nsAtom* aFamilyName instead?
    409 //
    410 // All callers have one handy.
    411 /* static */
    412 already_AddRefed<gfxUserFontEntry>
    413 FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace(
    414    FontFaceImpl* aFontFace, gfxUserFontAttributes&& aAttr,
    415    StyleOrigin aOrigin) {
    416  FontFaceSetImpl* set = aFontFace->GetPrimaryFontFaceSet();
    417 
    418  RefPtr<gfxUserFontEntry> existingEntry = aFontFace->GetUserFontEntry();
    419  if (existingEntry) {
    420    if (NS_IsMainThread()) {
    421      set->UpdateUserFontEntry(existingEntry, std::move(aAttr));
    422    } else {
    423      auto task =
    424          MakeRefPtr<UpdateUserFontEntryRunnable>(set, existingEntry, aAttr);
    425      IgnoredErrorResult ignoredRv;
    426      task->Dispatch(GetCurrentThreadWorkerPrivate(), Canceling, ignoredRv);
    427    }
    428    return existingEntry.forget();
    429  }
    430 
    431  // set up src array
    432  nsTArray<gfxFontFaceSrc> srcArray;
    433 
    434  if (aFontFace->HasFontData()) {
    435    gfxFontFaceSrc* face = srcArray.AppendElement();
    436    if (!face) {
    437      return nullptr;
    438    }
    439 
    440    face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
    441    face->mBuffer = aFontFace->TakeBufferSource();
    442  } else {
    443    size_t len = aAttr.mSources.Length();
    444    for (size_t i = 0; i < len; ++i) {
    445      gfxFontFaceSrc* face = srcArray.AppendElement();
    446      const auto& component = aAttr.mSources[i];
    447      switch (component.tag) {
    448        case StyleFontFaceSourceListComponent::Tag::Local: {
    449          nsAtom* atom = component.AsLocal();
    450          face->mLocalName.Append(nsAtomCString(atom));
    451          face->mSourceType = gfxFontFaceSrc::eSourceType_Local;
    452          face->mURI = nullptr;
    453          face->mFormatHint = StyleFontFaceSourceFormatKeyword::None;
    454          break;
    455        }
    456 
    457        case StyleFontFaceSourceListComponent::Tag::Url: {
    458          face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
    459          const StyleCssUrl* url = component.AsUrl();
    460          nsIURI* uri = url->GetURI();
    461          face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr;
    462          const URLExtraData& extraData = url->ExtraData();
    463          face->mReferrerInfo = extraData.ReferrerInfo();
    464 
    465          // agent and user stylesheets are treated slightly differently,
    466          // the same-site origin check and access control headers are
    467          // enforced against the sheet principal rather than the document
    468          // principal to allow user stylesheets to include @font-face rules
    469          if (aOrigin == StyleOrigin::User ||
    470              aOrigin == StyleOrigin::UserAgent) {
    471            face->mUseOriginPrincipal = true;
    472            face->mOriginPrincipal = new gfxFontSrcPrincipal(
    473                extraData.Principal(), extraData.Principal());
    474          }
    475 
    476          face->mLocalName.Truncate();
    477          face->mFormatHint = StyleFontFaceSourceFormatKeyword::None;
    478          face->mTechFlags = StyleFontFaceSourceTechFlags::Empty();
    479 
    480          if (i + 1 < len) {
    481            // Check for a format hint.
    482            const auto& next = aAttr.mSources[i + 1];
    483            switch (next.tag) {
    484              case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
    485                face->mFormatHint = next.format_hint_keyword._0;
    486                i++;
    487                break;
    488              case StyleFontFaceSourceListComponent::Tag::FormatHintString: {
    489                nsDependentCSubstring valueString(
    490                    reinterpret_cast<const char*>(
    491                        next.format_hint_string.utf8_bytes),
    492                    next.format_hint_string.length);
    493 
    494                if (valueString.LowerCaseEqualsASCII("woff")) {
    495                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff;
    496                } else if (valueString.LowerCaseEqualsASCII("woff2")) {
    497                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2;
    498                } else if (valueString.LowerCaseEqualsASCII("opentype")) {
    499                  face->mFormatHint =
    500                      StyleFontFaceSourceFormatKeyword::Opentype;
    501                } else if (valueString.LowerCaseEqualsASCII("truetype")) {
    502                  face->mFormatHint =
    503                      StyleFontFaceSourceFormatKeyword::Truetype;
    504                } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
    505                  face->mFormatHint =
    506                      StyleFontFaceSourceFormatKeyword::Truetype;
    507                } else if (valueString.LowerCaseEqualsASCII(
    508                               "embedded-opentype")) {
    509                  face->mFormatHint =
    510                      StyleFontFaceSourceFormatKeyword::EmbeddedOpentype;
    511                } else if (valueString.LowerCaseEqualsASCII("svg")) {
    512                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Svg;
    513                } else if (StaticPrefs::layout_css_font_variations_enabled()) {
    514                  // Non-standard values that Firefox accepted, for back-compat;
    515                  // these are superseded by the tech() function.
    516                  if (valueString.LowerCaseEqualsASCII("woff-variations")) {
    517                    face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff;
    518                  } else if (valueString.LowerCaseEqualsASCII(
    519                                 "woff2-variations")) {
    520                    face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2;
    521                  } else if (valueString.LowerCaseEqualsASCII(
    522                                 "opentype-variations")) {
    523                    face->mFormatHint =
    524                        StyleFontFaceSourceFormatKeyword::Opentype;
    525                  } else if (valueString.LowerCaseEqualsASCII(
    526                                 "truetype-variations")) {
    527                    face->mFormatHint =
    528                        StyleFontFaceSourceFormatKeyword::Truetype;
    529                  } else {
    530                    face->mFormatHint =
    531                        StyleFontFaceSourceFormatKeyword::Unknown;
    532                  }
    533                } else {
    534                  // unknown format specified, mark to distinguish from the
    535                  // case where no format hints are specified
    536                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Unknown;
    537                }
    538                i++;
    539                break;
    540              }
    541              case StyleFontFaceSourceListComponent::Tag::TechFlags:
    542              case StyleFontFaceSourceListComponent::Tag::Local:
    543              case StyleFontFaceSourceListComponent::Tag::Url:
    544                break;
    545            }
    546          }
    547 
    548          if (i + 1 < len) {
    549            // Check for a set of font-technologies flags.
    550            const auto& next = aAttr.mSources[i + 1];
    551            if (next.IsTechFlags()) {
    552              face->mTechFlags = next.AsTechFlags();
    553              i++;
    554            }
    555          }
    556 
    557          if (!face->mURI) {
    558            // if URI not valid, omit from src array
    559            srcArray.RemoveLastElement();
    560            NS_WARNING("null url in @font-face rule");
    561            continue;
    562          }
    563          break;
    564        }
    565 
    566        case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
    567        case StyleFontFaceSourceListComponent::Tag::FormatHintString:
    568        case StyleFontFaceSourceListComponent::Tag::TechFlags:
    569          MOZ_ASSERT_UNREACHABLE(
    570              "Should always come after a URL source, and be consumed already");
    571          break;
    572      }
    573    }
    574  }
    575 
    576  if (srcArray.IsEmpty()) {
    577    return nullptr;
    578  }
    579 
    580  return set->FindOrCreateUserFontEntry(std::move(srcArray), std::move(aAttr));
    581 }
    582 
    583 nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry,
    584                                     uint32_t aSrcIndex, const char* aMessage,
    585                                     uint32_t aFlags, nsresult aStatus) {
    586  nsAutoCString familyName;
    587  nsAutoCString fontURI;
    588  aUserFontEntry->GetFamilyNameAndURIForLogging(aSrcIndex, familyName, fontURI);
    589 
    590  nsAutoCString weightString;
    591  aUserFontEntry->Weight().ToString(weightString);
    592  nsAutoCString stretchString;
    593  aUserFontEntry->Stretch().ToString(stretchString);
    594  nsPrintfCString message(
    595      "downloadable font: %s "
    596      "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
    597      aMessage, familyName.get(),
    598      aUserFontEntry->IsItalic() ? "italic" : "normal",  // XXX todo: oblique?
    599      weightString.get(), stretchString.get(), aSrcIndex);
    600 
    601  if (NS_FAILED(aStatus)) {
    602    message.AppendLiteral(": ");
    603    switch (aStatus) {
    604      case NS_ERROR_DOM_BAD_URI:
    605        message.AppendLiteral("bad URI or cross-site access not allowed");
    606        break;
    607      case NS_ERROR_CONTENT_BLOCKED:
    608        message.AppendLiteral("content blocked");
    609        break;
    610      default:
    611        message.AppendLiteral("status=");
    612        message.AppendInt(static_cast<uint32_t>(aStatus));
    613        break;
    614    }
    615  }
    616  message.AppendLiteral(" source: ");
    617  message.Append(fontURI);
    618 
    619  LOG(("userfonts (%p) %s", this, message.get()));
    620 
    621  if (GetCurrentThreadWorkerPrivate()) {
    622    // TODO(aosmond): Log to the console for workers. See bug 1778537.
    623    return NS_OK;
    624  }
    625 
    626  nsCOMPtr<nsIConsoleService> console(
    627      do_GetService(NS_CONSOLESERVICE_CONTRACTID));
    628  if (!console) {
    629    return NS_ERROR_NOT_AVAILABLE;
    630  }
    631 
    632  // try to give the user an indication of where the rule came from
    633  StyleLockedFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
    634  nsAutoCString href;
    635  uint32_t line = 0;
    636  uint32_t column = 0;
    637  if (rule) {
    638    Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
    639    // FIXME We need to figure out an approach to get the style sheet
    640    // of this raw rule. See bug 1450903. Leave href empty if we don't know how
    641    // to get the correct sheet.
    642  }
    643 
    644  nsresult rv;
    645  nsCOMPtr<nsIScriptError> scriptError =
    646      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
    647  NS_ENSURE_SUCCESS(rv, rv);
    648 
    649  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
    650                                     href,  // file
    651                                     line, column,
    652                                     aFlags,        // flags
    653                                     "CSS Loader",  // category (make separate?)
    654                                     GetInnerWindowID());
    655  if (NS_SUCCEEDED(rv)) {
    656    console->LogMessage(scriptError);
    657  }
    658 
    659  return NS_OK;
    660 }
    661 
    662 nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
    663                                           const gfxFontFaceSrc* aFontFaceSrc,
    664                                           uint8_t*& aBuffer,
    665                                           uint32_t& aBufferLength) {
    666  nsCOMPtr<nsIChannel> channel;
    667  nsresult rv = CreateChannelForSyncLoadFontData(getter_AddRefs(channel),
    668                                                 aFontToLoad, aFontFaceSrc);
    669  NS_ENSURE_SUCCESS(rv, rv);
    670 
    671  // blocking stream is OK for data URIs
    672  nsCOMPtr<nsIInputStream> stream;
    673  rv = channel->Open(getter_AddRefs(stream));
    674  NS_ENSURE_SUCCESS(rv, rv);
    675 
    676  uint64_t bufferLength64;
    677  rv = stream->Available(&bufferLength64);
    678  NS_ENSURE_SUCCESS(rv, rv);
    679  if (bufferLength64 == 0) {
    680    return NS_ERROR_FAILURE;
    681  }
    682  if (bufferLength64 > UINT32_MAX) {
    683    return NS_ERROR_FILE_TOO_BIG;
    684  }
    685  aBufferLength = static_cast<uint32_t>(bufferLength64);
    686 
    687  // read all the decoded data
    688  aBuffer = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * aBufferLength));
    689  if (!aBuffer) {
    690    aBufferLength = 0;
    691    return NS_ERROR_OUT_OF_MEMORY;
    692  }
    693 
    694  uint32_t numRead, totalRead = 0;
    695  while (NS_SUCCEEDED(
    696             rv = stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
    697                               aBufferLength - totalRead, &numRead)) &&
    698         numRead != 0) {
    699    totalRead += numRead;
    700    if (totalRead > aBufferLength) {
    701      rv = NS_ERROR_FAILURE;
    702      break;
    703    }
    704  }
    705 
    706  // make sure there's a mime type
    707  if (NS_SUCCEEDED(rv)) {
    708    nsAutoCString mimeType;
    709    rv = channel->GetContentType(mimeType);
    710    aBufferLength = totalRead;
    711  }
    712 
    713  if (NS_FAILED(rv)) {
    714    free(aBuffer);
    715    aBuffer = nullptr;
    716    aBufferLength = 0;
    717    return rv;
    718  }
    719 
    720  return NS_OK;
    721 }
    722 
    723 void FontFaceSetImpl::OnFontFaceStatusChanged(FontFaceImpl* aFontFace) {
    724  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
    725  RecursiveMutexAutoLock lock(mMutex);
    726  MOZ_ASSERT(HasAvailableFontFace(aFontFace));
    727 
    728  mHasLoadingFontFacesIsDirty = true;
    729 
    730  if (aFontFace->Status() == FontFaceLoadStatus::Loading) {
    731    CheckLoadingStarted();
    732  } else {
    733    MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded ||
    734               aFontFace->Status() == FontFaceLoadStatus::Error);
    735    // When a font finishes downloading, nsPresContext::UserFontSetUpdated
    736    // will be called immediately afterwards to request a reflow of the
    737    // relevant elements in the document.  We want to wait until the reflow
    738    // request has been done before the FontFaceSet is marked as Loaded so
    739    // that we don't briefly set the FontFaceSet to Loaded and then Loading
    740    // again once the reflow is pending.  So we go around the event loop
    741    // and call CheckLoadingFinished() after the reflow has been queued.
    742    if (!mDelayedLoadCheck) {
    743      mDelayedLoadCheck = true;
    744      DispatchCheckLoadingFinishedAfterDelay();
    745    }
    746  }
    747 }
    748 
    749 void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() {
    750  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
    751 
    752  if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) {
    753    // See comments in Gecko_GetFontMetrics.
    754    //
    755    // We can't just dispatch the runnable below if we're not on the main
    756    // thread, since it needs to take a strong reference to the FontFaceSet,
    757    // and being a DOM object, FontFaceSet doesn't support thread-safe
    758    // refcounting.
    759    set->AppendTask(
    760        PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(
    761            this));
    762    return;
    763  }
    764 
    765  DispatchToOwningThread(
    766      "FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay",
    767      [self = RefPtr{this}]() { self->CheckLoadingFinishedAfterDelay(); });
    768 }
    769 
    770 void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() {
    771  RecursiveMutexAutoLock lock(mMutex);
    772  mDelayedLoadCheck = false;
    773  CheckLoadingFinished();
    774 }
    775 
    776 void FontFaceSetImpl::CheckLoadingStarted() {
    777  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
    778  RecursiveMutexAutoLock lock(mMutex);
    779 
    780  if (!HasLoadingFontFaces()) {
    781    return;
    782  }
    783 
    784  if (mStatus == FontFaceSetLoadStatus::Loading) {
    785    // We have already dispatched a loading event and replaced mReady
    786    // with a fresh, unresolved promise.
    787    return;
    788  }
    789 
    790  mStatus = FontFaceSetLoadStatus::Loading;
    791 
    792  if (IsOnOwningThread()) {
    793    OnLoadingStarted();
    794    return;
    795  }
    796 
    797  DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted",
    798                         [self = RefPtr{this}]() { self->OnLoadingStarted(); });
    799 }
    800 
    801 void FontFaceSetImpl::OnLoadingStarted() {
    802  RecursiveMutexAutoLock lock(mMutex);
    803  if (mOwner) {
    804    mOwner->DispatchLoadingEventAndReplaceReadyPromise();
    805  }
    806 }
    807 
    808 void FontFaceSetImpl::UpdateHasLoadingFontFaces() {
    809  RecursiveMutexAutoLock lock(mMutex);
    810  mHasLoadingFontFacesIsDirty = false;
    811  mHasLoadingFontFaces = false;
    812  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
    813    if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) {
    814      mHasLoadingFontFaces = true;
    815      return;
    816    }
    817  }
    818 }
    819 
    820 bool FontFaceSetImpl::HasLoadingFontFaces() {
    821  RecursiveMutexAutoLock lock(mMutex);
    822  if (mHasLoadingFontFacesIsDirty) {
    823    UpdateHasLoadingFontFaces();
    824  }
    825  return mHasLoadingFontFaces;
    826 }
    827 
    828 bool FontFaceSetImpl::MightHavePendingFontLoads() {
    829  // Check for FontFace objects in the FontFaceSet that are still loading.
    830  return HasLoadingFontFaces();
    831 }
    832 
    833 void FontFaceSetImpl::CheckLoadingFinished() {
    834  RecursiveMutexAutoLock lock(mMutex);
    835  if (mDelayedLoadCheck) {
    836    // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
    837    return;
    838  }
    839 
    840  if (!ReadyPromiseIsPending()) {
    841    // We've already resolved mReady (or set the flag to do that lazily) and
    842    // dispatched the loadingdone/loadingerror events.
    843    return;
    844  }
    845 
    846  if (MightHavePendingFontLoads()) {
    847    // We're not finished loading yet.
    848    return;
    849  }
    850 
    851  mStatus = FontFaceSetLoadStatus::Loaded;
    852 
    853  if (IsOnOwningThread()) {
    854    OnLoadingFinished();
    855    return;
    856  }
    857 
    858  DispatchToOwningThread(
    859      "FontFaceSetImpl::CheckLoadingFinished",
    860      [self = RefPtr{this}]() { self->OnLoadingFinished(); });
    861 }
    862 
    863 void FontFaceSetImpl::OnLoadingFinished() {
    864  RecursiveMutexAutoLock lock(mMutex);
    865  if (mOwner) {
    866    mOwner->MaybeResolve();
    867  }
    868 }
    869 
    870 void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
    871  RecursiveMutexAutoLock lock(mMutex);
    872  mAllowedFontLoads.Clear();
    873  IncrementGenerationLocked(false);
    874 }
    875 
    876 // -- gfxUserFontSet
    877 // ------------------------------------------------
    878 
    879 already_AddRefed<gfxFontSrcPrincipal>
    880 FontFaceSetImpl::GetStandardFontLoadPrincipal() const {
    881  RecursiveMutexAutoLock lock(mMutex);
    882  return RefPtr{mStandardFontLoadPrincipal}.forget();
    883 }
    884 
    885 void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize,
    886                                         TimeStamp aDoneTime) {
    887  mDownloadCount++;
    888  mDownloadSize += aFontSize;
    889  glean::webfont::size.Accumulate(aFontSize / 1024);
    890 
    891  TimeStamp navStart = GetNavigationStartTimeStamp();
    892  TimeStamp zero;
    893  if (navStart != zero) {
    894    mozilla::glean::network::font_download_end.AccumulateRawDuration(aDoneTime -
    895                                                                     navStart);
    896  }
    897 }
    898 
    899 void FontFaceSetImpl::DoRebuildUserFontSet() { MarkUserFontSetDirty(); }
    900 
    901 already_AddRefed<gfxUserFontEntry> FontFaceSetImpl::CreateUserFontEntry(
    902    nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList,
    903    gfxUserFontAttributes&& aAttr) {
    904  RefPtr<gfxUserFontEntry> entry = new FontFaceImpl::Entry(
    905      this, std::move(aFontFaceSrcList), std::move(aAttr));
    906  return entry.forget();
    907 }
    908 
    909 void FontFaceSetImpl::ForgetLocalFaces() {
    910  // We cannot hold our lock at the same time as the gfxUserFontFamily lock, so
    911  // we need to make a copy of the table first.
    912  nsTArray<RefPtr<gfxUserFontFamily>> fontFamilies;
    913  {
    914    RecursiveMutexAutoLock lock(mMutex);
    915    fontFamilies.SetCapacity(mFontFamilies.Count());
    916    for (const auto& fam : mFontFamilies.Values()) {
    917      fontFamilies.AppendElement(fam);
    918    }
    919  }
    920 
    921  for (const auto& fam : fontFamilies) {
    922    ForgetLocalFace(fam);
    923  }
    924 }
    925 
    926 already_AddRefed<gfxUserFontFamily> FontFaceSetImpl::GetFamily(
    927    const nsACString& aFamilyName) {
    928  RecursiveMutexAutoLock lock(mMutex);
    929  return gfxUserFontSet::GetFamily(aFamilyName);
    930 }
    931 
    932 #undef LOG_ENABLED
    933 #undef LOG