tor-browser

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

FontFaceSetDocumentImpl.cpp (25917B)


      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 "FontFaceSetDocumentImpl.h"
      8 
      9 #include "mozilla/FontLoaderUtils.h"
     10 #include "mozilla/LoadInfo.h"
     11 #include "mozilla/PresShell.h"
     12 #include "mozilla/PresShellInlines.h"
     13 #include "mozilla/css/Loader.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/DocumentInlines.h"
     16 #include "mozilla/dom/Event.h"
     17 #include "mozilla/dom/FontFaceImpl.h"
     18 #include "mozilla/dom/FontFaceSet.h"
     19 #include "nsContentPolicyUtils.h"
     20 #include "nsDOMNavigationTiming.h"
     21 #include "nsFontFaceLoader.h"
     22 #include "nsIDocShell.h"
     23 #include "nsISupportsPriority.h"
     24 #include "nsIWebNavigation.h"
     25 #include "nsPresContext.h"
     26 
     27 using namespace mozilla;
     28 using namespace mozilla::css;
     29 using namespace mozilla::dom;
     30 
     31 #define LOG(args) \
     32  MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
     33 #define LOG_ENABLED() \
     34  MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
     35 
     36 NS_IMPL_ISUPPORTS_INHERITED(FontFaceSetDocumentImpl, FontFaceSetImpl,
     37                            nsIDOMEventListener, nsICSSLoaderObserver)
     38 
     39 FontFaceSetDocumentImpl::FontFaceSetDocumentImpl(FontFaceSet* aOwner,
     40                                                 dom::Document* aDocument)
     41    : FontFaceSetImpl(aOwner), mDocument(aDocument) {}
     42 
     43 FontFaceSetDocumentImpl::~FontFaceSetDocumentImpl() = default;
     44 
     45 void FontFaceSetDocumentImpl::Initialize() {
     46  RecursiveMutexAutoLock lock(mMutex);
     47 
     48  MOZ_ASSERT(mDocument, "We should get a valid document from the caller!");
     49 
     50  // Record the state of the "bypass cache" flags from the docshell now,
     51  // since we want to look at them from style worker threads, and we can
     52  // only get to the docshell through a weak pointer (which is only
     53  // possible on the main thread).
     54  //
     55  // In theory the load type of a docshell could change after the document
     56  // is loaded, but handling that doesn't seem too important.
     57  if (nsCOMPtr<nsIDocShell> docShell = mDocument->GetDocShell()) {
     58    uint32_t loadType;
     59    uint32_t flags;
     60    if ((NS_SUCCEEDED(docShell->GetLoadType(&loadType)) &&
     61         ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)) ||
     62        (NS_SUCCEEDED(docShell->GetDefaultLoadFlags(&flags)) &&
     63         (flags & nsIRequest::LOAD_BYPASS_CACHE))) {
     64      mBypassCache = true;
     65    }
     66  }
     67 
     68  // Same for the "private browsing" flag.
     69  if (nsCOMPtr<nsILoadContext> loadContext = mDocument->GetLoadContext()) {
     70    mPrivateBrowsing = loadContext->UsePrivateBrowsing();
     71  }
     72 
     73  if (!mDocument->DidFireDOMContentLoaded()) {
     74    mDocument->AddSystemEventListener(u"DOMContentLoaded"_ns, this, false,
     75                                      false);
     76  } else {
     77    // In some cases we can't rely on CheckLoadingFinished being called from
     78    // the refresh driver.  For example, documents in display:none iframes.
     79    // Or if the document has finished loading and painting at the time that
     80    // script requests document.fonts and causes us to get here.
     81    CheckLoadingFinished();
     82  }
     83 
     84  mDocument->EnsureCSSLoader().AddObserver(this);
     85 
     86  mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>(
     87      mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
     88 }
     89 
     90 void FontFaceSetDocumentImpl::Destroy() {
     91  RemoveDOMContentLoadedListener();
     92 
     93  if (mDocument && mDocument->GetExistingCSSLoader()) {
     94    // We're null checking CSSLoader() since FontFaceSetImpl::Disconnect() might
     95    // be being called during unlink, at which time the loader may already have
     96    // been unlinked from the document.
     97    mDocument->GetExistingCSSLoader()->RemoveObserver(this);
     98  }
     99 
    100  mRuleFaces.Clear();
    101 
    102  // Since the base class may depend on the document remaining set, we need to
    103  // clear mDocument after.
    104  FontFaceSetImpl::Destroy();
    105 
    106  mDocument = nullptr;
    107 }
    108 
    109 bool FontFaceSetDocumentImpl::IsOnOwningThread() { return NS_IsMainThread(); }
    110 
    111 #ifdef DEBUG
    112 void FontFaceSetDocumentImpl::AssertIsOnOwningThread() {
    113  MOZ_ASSERT(NS_IsMainThread());
    114 }
    115 #endif
    116 
    117 void FontFaceSetDocumentImpl::DispatchToOwningThread(
    118    const char* aName, std::function<void()>&& aFunc) {
    119  class FontFaceSetDocumentRunnable final : public Runnable {
    120   public:
    121    FontFaceSetDocumentRunnable(const char* aName,
    122                                std::function<void()>&& aFunc)
    123        : Runnable(aName), mFunc(std::move(aFunc)) {}
    124 
    125    NS_IMETHOD Run() final {
    126      mFunc();
    127      return NS_OK;
    128    }
    129 
    130   private:
    131    std::function<void()> mFunc;
    132  };
    133 
    134  auto runnable =
    135      MakeRefPtr<FontFaceSetDocumentRunnable>(aName, std::move(aFunc));
    136  NS_DispatchToMainThread(runnable.forget());
    137 }
    138 
    139 uint64_t FontFaceSetDocumentImpl::GetInnerWindowID() {
    140  MOZ_ASSERT(NS_IsMainThread());
    141  if (!mDocument) {
    142    return 0;
    143  }
    144 
    145  return mDocument->InnerWindowID();
    146 }
    147 
    148 FontVisibilityProvider* FontFaceSetDocumentImpl::GetFontVisibilityProvider()
    149    const {
    150  mozilla::AssertIsMainThreadOrServoFontMetricsLocked();
    151  if (!mDocument) {
    152    return nullptr;
    153  }
    154 
    155  return mDocument->GetPresContext();
    156 }
    157 
    158 void FontFaceSetDocumentImpl::RefreshStandardFontLoadPrincipal() {
    159  MOZ_ASSERT(NS_IsMainThread());
    160  RecursiveMutexAutoLock lock(mMutex);
    161  if (NS_WARN_IF(!mDocument)) {
    162    return;
    163  }
    164  mStandardFontLoadPrincipal = MakeRefPtr<gfxFontSrcPrincipal>(
    165      mDocument->NodePrincipal(), mDocument->PartitionedPrincipal());
    166  FontFaceSetImpl::RefreshStandardFontLoadPrincipal();
    167 }
    168 
    169 already_AddRefed<URLExtraData> FontFaceSetDocumentImpl::GetURLExtraData() {
    170  if (!mDocument) {
    171    return nullptr;
    172  }
    173  return do_AddRef(mDocument->DefaultStyleAttrURLData());
    174 }
    175 
    176 void FontFaceSetDocumentImpl::RemoveDOMContentLoadedListener() {
    177  if (mDocument) {
    178    mDocument->RemoveSystemEventListener(u"DOMContentLoaded"_ns, this, false);
    179  }
    180 }
    181 
    182 void FontFaceSetDocumentImpl::FindMatchingFontFaces(
    183    const nsTHashSet<FontFace*>& aMatchingFaces,
    184    nsTArray<FontFace*>& aFontFaces) {
    185  FontFaceSetImpl::FindMatchingFontFaces(aMatchingFaces, aFontFaces);
    186  for (FontFaceRecord& record : mRuleFaces) {
    187    FontFace* owner = record.mFontFace->GetOwner();
    188    if (owner && aMatchingFaces.Contains(owner)) {
    189      aFontFaces.AppendElement(owner);
    190    }
    191  }
    192 }
    193 
    194 TimeStamp FontFaceSetDocumentImpl::GetNavigationStartTimeStamp() {
    195  TimeStamp navStart;
    196  RefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
    197  if (timing) {
    198    navStart = timing->GetNavigationStartTimeStamp();
    199  }
    200  return navStart;
    201 }
    202 
    203 void FontFaceSetDocumentImpl::EnsureReady() {
    204  MOZ_ASSERT(NS_IsMainThread());
    205 
    206  // There may be outstanding style changes that will trigger the loading of
    207  // new fonts.  We need to flush layout to initiate any such loads so that
    208  // if mReady is currently resolved we replace it with a new pending Promise.
    209  // (That replacement will happen under this flush call.)
    210  if (!ReadyPromiseIsPending() && mDocument) {
    211    mDocument->FlushPendingNotifications(FlushType::Layout);
    212  }
    213 }
    214 
    215 #ifdef DEBUG
    216 bool FontFaceSetDocumentImpl::HasRuleFontFace(FontFaceImpl* aFontFace) {
    217  for (const auto& record : mRuleFaces) {
    218    if (record.mFontFace == aFontFace) {
    219      return true;
    220    }
    221  }
    222  return false;
    223 }
    224 #endif
    225 
    226 bool FontFaceSetDocumentImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
    227  if (NS_WARN_IF(!mDocument)) {
    228    return false;
    229  }
    230 
    231  if (!FontFaceSetImpl::Add(aFontFace, aRv)) {
    232    return false;
    233  }
    234 
    235  RefPtr<dom::Document> clonedDoc = mDocument->GetLatestStaticClone();
    236  if (clonedDoc) {
    237    // The document is printing, copy the font to the static clone as well.
    238    nsCOMPtr<nsIPrincipal> principal = mDocument->GetPrincipal();
    239    if (principal->IsSystemPrincipal() || nsContentUtils::IsPDFJS(principal)) {
    240      ErrorResult rv;
    241      clonedDoc->Fonts()->Add(*aFontFace->GetOwner(), rv);
    242      MOZ_ASSERT(!rv.Failed());
    243    }
    244  }
    245 
    246  return true;
    247 }
    248 
    249 nsresult FontFaceSetDocumentImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
    250                                            uint32_t aSrcIndex) {
    251  if (NS_WARN_IF(!mDocument)) {
    252    return NS_ERROR_FAILURE;
    253  }
    254 
    255  nsresult rv;
    256 
    257  nsCOMPtr<nsIStreamLoader> streamLoader;
    258  RefPtr<nsFontFaceLoader> fontLoader;
    259 
    260  const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex);
    261 
    262  auto preloadKey =
    263      PreloadHashKey::CreateAsFont(src.mURI->get(), CORS_ANONYMOUS);
    264  RefPtr<PreloaderBase> preload =
    265      mDocument->Preloads().LookupPreload(preloadKey);
    266 
    267  if (preload) {
    268    fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this,
    269                                      preload->Channel());
    270    rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
    271                            fontLoader);
    272    NS_ENSURE_SUCCESS(rv, rv);
    273 
    274    rv = preload->AsyncConsume(streamLoader);
    275 
    276    // We don't want this to hang around regardless of the result, there will be
    277    // no coalescing of later found <link preload> tags for fonts.
    278    preload->RemoveSelf(mDocument);
    279  } else {
    280    // No preload found, open a channel.
    281    rv = NS_ERROR_FAILURE;
    282  }
    283 
    284  nsCOMPtr<nsILoadGroup> loadGroup(mDocument->GetDocumentLoadGroup());
    285  if (NS_FAILED(rv)) {
    286    nsCOMPtr<nsIChannel> channel;
    287    rv = FontLoaderUtils::BuildChannel(
    288        getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
    289        dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
    290        mDocument, loadGroup, nullptr, false,
    291        nsISupportsPriority::PRIORITY_HIGH);
    292    NS_ENSURE_SUCCESS(rv, rv);
    293 
    294    fontLoader = new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel);
    295 
    296    if (LOG_ENABLED()) {
    297      nsCOMPtr<nsIURI> referrer = src.mReferrerInfo
    298                                      ? src.mReferrerInfo->GetOriginalReferrer()
    299                                      : nullptr;
    300      LOG((
    301          "userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
    302          fontLoader.get(), src.mURI->GetSpecOrDefault().get(),
    303          referrer ? referrer->GetSpecOrDefault().get() : ""));
    304    }
    305 
    306    rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader,
    307                            fontLoader);
    308    NS_ENSURE_SUCCESS(rv, rv);
    309 
    310    rv = channel->AsyncOpen(streamLoader);
    311    if (NS_FAILED(rv)) {
    312      fontLoader->DropChannel();  // explicitly need to break ref cycle
    313    }
    314  }
    315 
    316  {
    317    RecursiveMutexAutoLock lock(mMutex);
    318    mLoaders.PutEntry(fontLoader);
    319  }
    320 
    321  if (NS_SUCCEEDED(rv)) {
    322    fontLoader->StartedLoading(streamLoader);
    323    // let the font entry remember the loader, in case we need to cancel it
    324    aUserFontEntry->SetLoader(fontLoader);
    325  }
    326 
    327  return rv;
    328 }
    329 
    330 bool FontFaceSetDocumentImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) {
    331  MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL);
    332 
    333  if (ServoStyleSet::IsInServoTraversal()) {
    334    RecursiveMutexAutoLock lock(mMutex);
    335    auto entry = mAllowedFontLoads.Lookup(&aSrc);
    336    MOZ_DIAGNOSTIC_ASSERT(entry, "Missed an update?");
    337    return entry ? *entry : false;
    338  }
    339 
    340  MOZ_ASSERT(NS_IsMainThread());
    341 
    342  if (aSrc.mUseOriginPrincipal) {
    343    return true;
    344  }
    345 
    346  if (NS_WARN_IF(!mDocument)) {
    347    return false;
    348  }
    349 
    350  RefPtr<gfxFontSrcPrincipal> gfxPrincipal =
    351      aSrc.mURI->InheritsSecurityContext() ? nullptr
    352                                           : aSrc.LoadPrincipal(*this);
    353 
    354  nsIPrincipal* principal =
    355      gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr;
    356 
    357  Result<RefPtr<net::LoadInfo>, nsresult> maybeLoadInfo = net::LoadInfo::Create(
    358      mDocument->NodePrincipal(),  // loading principal
    359      principal,                   // triggering principal
    360      mDocument, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
    361      nsIContentPolicy::TYPE_FONT);
    362  if (NS_WARN_IF(maybeLoadInfo.isErr())) {
    363    return false;
    364  }
    365  RefPtr<net::LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap();
    366 
    367  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
    368  nsresult rv =
    369      NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &shouldLoad,
    370                                nsContentUtils::GetContentPolicy());
    371 
    372  return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
    373 }
    374 
    375 nsresult FontFaceSetDocumentImpl::CreateChannelForSyncLoadFontData(
    376    nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
    377    const gfxFontFaceSrc* aFontFaceSrc) {
    378  gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
    379 
    380  // Note we are calling NS_NewChannelWithTriggeringPrincipal() with both a
    381  // node and a principal.  This is because the document where the font is
    382  // being loaded might have a different origin from the principal of the
    383  // stylesheet that initiated the font load.
    384  // Further, we only get here for data: loads, so it doesn't really matter
    385  // whether we use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be
    386  // more restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
    387  return NS_NewChannelWithTriggeringPrincipal(
    388      aOutChannel, aFontFaceSrc->mURI->get(), mDocument,
    389      principal ? principal->NodePrincipal() : nullptr,
    390      nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
    391      aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT
    392                                        : nsIContentPolicy::TYPE_FONT);
    393 }
    394 
    395 bool FontFaceSetDocumentImpl::UpdateRules(
    396    const nsTArray<nsFontFaceRuleContainer>& aRules) {
    397  RecursiveMutexAutoLock lock(mMutex);
    398 
    399  // If there was a change to the mNonRuleFaces array, then there could
    400  // have been a modification to the user font set.
    401  bool modified = mNonRuleFacesDirty;
    402  mNonRuleFacesDirty = false;
    403 
    404  // The @font-face rules that make up the user font set have changed,
    405  // so we need to update the set. However, we want to preserve existing
    406  // font entries wherever possible, so that we don't discard and then
    407  // re-download resources in the (common) case where at least some of the
    408  // same rules are still present.
    409  nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
    410 
    411  // We reverse the oldRecords array because we will most likely be using the
    412  // entries in the order they were originally added, and constantly removing
    413  // the first element is inefficient if the array is large; it's better if
    414  // we're most often removing elements from the end.
    415  oldRecords.Reverse();
    416 
    417  // Remove faces from the font family records; we need to re-insert them
    418  // because we might end up with faces in a different order even if they're
    419  // the same font entries as before. (The order can affect font selection
    420  // where multiple faces match the requested style, perhaps with overlapping
    421  // unicode-range coverage.)
    422  for (const auto& fontFamily : mFontFamilies.Values()) {
    423    fontFamily->DetachFontEntries();
    424  }
    425 
    426  // Sometimes aRules has duplicate @font-face rules in it; we should make
    427  // that not happen, but in the meantime, don't try to insert the same
    428  // FontFace object more than once into mRuleFaces.  We track which
    429  // ones we've handled in this table.
    430  nsTHashSet<StyleLockedFontFaceRule*> handledRules;
    431 
    432  for (const nsFontFaceRuleContainer& container : aRules) {
    433    // Insert each FontFace objects for each rule into our list, migrating old
    434    // font entries if possible rather than creating new ones; set  modified  to
    435    // true if we detect that rule ordering has changed, or if a new entry is
    436    // created.
    437    StyleLockedFontFaceRule* rule = container.mRule;
    438    if (!handledRules.EnsureInserted(rule)) {
    439      // rule was already present in the hashtable
    440      continue;
    441    }
    442    modified |= InsertRuleFontFace(rule, container.mOrigin, oldRecords);
    443  }
    444 
    445  for (const FontFaceRecord& record : mNonRuleFaces) {
    446    // Do the same for the non rule backed FontFace objects.
    447    InsertNonRuleFontFace(record.mFontFace);
    448  }
    449 
    450  // Remove any residual families that have no font entries (i.e., they were
    451  // not defined at all by the updated set of @font-face rules).
    452  for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
    453    if (!it.Data()->FontListLength()) {
    454      it.Remove();
    455    }
    456  }
    457 
    458  // If any FontFace objects for rules are left in the old list, note that the
    459  // set has changed (even if the new set was built entirely by migrating old
    460  // font entries).
    461  if (!oldRecords.IsEmpty()) {
    462    modified = true;
    463    // Any in-progress loaders for obsolete rules should be cancelled,
    464    // as the resource being downloaded will no longer be required.
    465    // We need to explicitly remove any loaders here, otherwise the loaders
    466    // will keep their "orphaned" font entries alive until they complete,
    467    // even after the oldRules array is deleted.
    468    //
    469    // XXX Now that it is possible for the author to hold on to a rule backed
    470    // FontFace object, we shouldn't cancel loading here; instead we should do
    471    // it when the FontFace is GCed, if we can detect that.
    472    for (const FontFaceRecord& record : oldRecords) {
    473      RefPtr<FontFaceImpl> f = record.mFontFace;
    474      if (gfxUserFontEntry* userFontEntry = f->GetUserFontEntry()) {
    475        if (nsFontFaceLoader* loader = userFontEntry->GetLoader()) {
    476          loader->Cancel();
    477          RemoveLoader(loader);
    478        }
    479      }
    480 
    481      // Any left over FontFace objects should also cease being rule backed.
    482      f->DisconnectFromRule();
    483    }
    484  }
    485 
    486  if (modified) {
    487    IncrementGenerationLocked(true);
    488    mHasLoadingFontFacesIsDirty = true;
    489    CheckLoadingStarted();
    490    CheckLoadingFinished();
    491  }
    492 
    493  // if local rules needed to be rebuilt, they have been rebuilt at this point
    494  if (mRebuildLocalRules) {
    495    mLocalRulesUsed = false;
    496    mRebuildLocalRules = false;
    497  }
    498 
    499  if (LOG_ENABLED() && !mRuleFaces.IsEmpty()) {
    500    LOG(("userfonts (%p) userfont rules update (%s) rule count: %d", this,
    501         (modified ? "modified" : "not modified"), (int)(mRuleFaces.Length())));
    502  }
    503 
    504  return modified;
    505 }
    506 
    507 bool FontFaceSetDocumentImpl::InsertRuleFontFace(
    508    StyleLockedFontFaceRule* aRule, StyleOrigin aSheetType,
    509    nsTArray<FontFaceRecord>& aOldRecords) {
    510  RecursiveMutexAutoLock lock(mMutex);
    511 
    512  if (MOZ_UNLIKELY(!mOwner)) {
    513    return false;
    514  }
    515 
    516  gfxUserFontAttributes attr;
    517  if (!FontFaceImpl::GetAttributesFromRule(aRule, attr)) {
    518    // If there is no family name, this rule cannot contribute a
    519    // usable font, so there is no point in processing it further.
    520    return false;
    521  }
    522 
    523  bool remove = false;
    524  size_t removeIndex;
    525 
    526  // This is a rule backed FontFace.  First, we check in aOldRecords; if
    527  // the FontFace for the rule exists there, just move it to the new record
    528  // list, and put the entry into the appropriate family.
    529  // Note that aOldRecords was reversed, so we search it from the end.
    530  for (size_t i = aOldRecords.Length(); i > 0;) {
    531    FontFaceRecord& rec = aOldRecords[--i];
    532 
    533    const bool matches =
    534        rec.mOrigin == Some(aSheetType) &&
    535        Servo_FontFaceRule_Equals(rec.mFontFace->GetData(), aRule);
    536    if (!matches) {
    537      continue;
    538    }
    539 
    540    FontFace* owner = rec.mFontFace->GetOwner();
    541    // if local rules were used, don't use the old font entry
    542    // for rules containing src local usage
    543    if (mLocalRulesUsed && mRebuildLocalRules) {
    544      const bool hasLocalSource = [&] {
    545        for (auto& source : attr.mSources) {
    546          if (source.IsLocal()) {
    547            return true;
    548          }
    549        }
    550        return false;
    551      }();
    552 
    553      if (hasLocalSource) {
    554        // Remove the old record, but wait to see if we successfully create a
    555        // new user font entry below.
    556        remove = true;
    557        removeIndex = i;
    558        break;
    559      }
    560    }
    561 
    562    rec.mFontFace->SetRule(aRule);
    563    gfxUserFontEntry* entry = rec.mFontFace->GetUserFontEntry();
    564    MOZ_ASSERT(entry, "FontFace should have a gfxUserFontEntry by now");
    565 
    566    AddUserFontEntry(attr.mFamilyName, entry);
    567 
    568    MOZ_ASSERT(!HasRuleFontFace(rec.mFontFace),
    569               "FontFace should not occur in mRuleFaces twice");
    570 
    571    mRuleFaces.AppendElement(rec);
    572    aOldRecords.RemoveElementAt(i);
    573 
    574    if (owner) {
    575      mOwner->InsertRuleFontFace(owner, aSheetType);
    576    }
    577 
    578    // Return that the set has been modified if an old rule was skipped to find
    579    // this one: something has been dropped, or ordering changed.
    580    // Note that the record at index i has been removed, so Length() is now the
    581    // original last-element index.
    582    return i < aOldRecords.Length();
    583  }
    584 
    585  RefPtr<FontFace> fontFace =
    586      FontFace::CreateForRule(mOwner->GetParentObject(), mOwner, aRule);
    587  RefPtr<FontFaceImpl> impl = fontFace->GetImpl();
    588  // this is a new rule:
    589  nsAutoCString family(attr.mFamilyName);
    590  RefPtr<gfxUserFontEntry> entry =
    591      FindOrCreateUserFontEntryFromFontFace(impl, std::move(attr), aSheetType);
    592 
    593  if (!entry) {
    594    return false;
    595  }
    596 
    597  if (remove) {
    598    // Although we broke out of the aOldRecords loop above, since we found
    599    // src local usage, and we're not using the old user font entry, we still
    600    // are adding a record to mRuleFaces with the same FontFace object.
    601    // Remove the old record so that we don't have the same FontFace listed
    602    // in both mRuleFaces and oldRecords, which would cause us to call
    603    // DisconnectFromRule on a FontFace that should still be rule backed.
    604    aOldRecords.RemoveElementAt(removeIndex);
    605  }
    606 
    607  FontFaceRecord rec;
    608  rec.mFontFace = impl;
    609  rec.mOrigin = Some(aSheetType);
    610 
    611  impl->SetUserFontEntry(entry);
    612 
    613  MOZ_ASSERT(!HasRuleFontFace(impl),
    614             "FontFace should not occur in mRuleFaces twice");
    615 
    616  mRuleFaces.AppendElement(rec);
    617 
    618  mOwner->InsertRuleFontFace(fontFace, aSheetType);
    619 
    620  // Add the entry to the end of the list.  If an existing userfont entry was
    621  // returned by FindOrCreateUserFontEntryFromFontFace that was already stored
    622  // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
    623  // calls, will automatically remove the earlier occurrence of the same
    624  // userfont entry.
    625  AddUserFontEntry(family, entry);
    626  return true;
    627 }
    628 
    629 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForEntry(
    630    gfxFontEntry* aFontEntry) {
    631  NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
    632  for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
    633    FontFaceImpl* f = mRuleFaces[i].mFontFace;
    634    gfxUserFontEntry* entry = f->GetUserFontEntry();
    635    if (entry && entry->GetPlatformFontEntry() == aFontEntry) {
    636      return f->GetRule();
    637    }
    638  }
    639  return nullptr;
    640 }
    641 
    642 StyleLockedFontFaceRule* FontFaceSetDocumentImpl::FindRuleForUserFontEntry(
    643    gfxUserFontEntry* aUserFontEntry) {
    644  for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
    645    FontFaceImpl* f = mRuleFaces[i].mFontFace;
    646    if (f->GetUserFontEntry() == aUserFontEntry) {
    647      return f->GetRule();
    648    }
    649  }
    650  return nullptr;
    651 }
    652 
    653 void FontFaceSetDocumentImpl::CacheFontLoadability() {
    654  RecursiveMutexAutoLock lock(mMutex);
    655 
    656  // TODO(emilio): We could do it a bit more incrementally maybe?
    657  for (const auto& fontFamily : mFontFamilies.Values()) {
    658    fontFamily->ReadLock();
    659    for (const gfxFontEntry* entry : fontFamily->GetFontList()) {
    660      if (!entry->mIsUserFontContainer) {
    661        continue;
    662      }
    663 
    664      const auto& sourceList =
    665          static_cast<const gfxUserFontEntry*>(entry)->SourceList();
    666      for (const gfxFontFaceSrc& src : sourceList) {
    667        if (src.mSourceType != gfxFontFaceSrc::eSourceType_URL) {
    668          continue;
    669        }
    670        mAllowedFontLoads.LookupOrInsertWith(
    671            &src, [&] { return IsFontLoadAllowed(src); });
    672      }
    673    }
    674    fontFamily->ReadUnlock();
    675  }
    676 }
    677 
    678 void FontFaceSetDocumentImpl::DidRefresh() { CheckLoadingFinished(); }
    679 
    680 void FontFaceSetDocumentImpl::UpdateHasLoadingFontFaces() {
    681  RecursiveMutexAutoLock lock(mMutex);
    682  FontFaceSetImpl::UpdateHasLoadingFontFaces();
    683 
    684  if (mHasLoadingFontFaces) {
    685    return;
    686  }
    687 
    688  for (size_t i = 0; i < mRuleFaces.Length(); i++) {
    689    FontFaceImpl* f = mRuleFaces[i].mFontFace;
    690    if (f->Status() == FontFaceLoadStatus::Loading) {
    691      mHasLoadingFontFaces = true;
    692      return;
    693    }
    694  }
    695 }
    696 
    697 bool FontFaceSetDocumentImpl::MightHavePendingFontLoads() {
    698  if (FontFaceSetImpl::MightHavePendingFontLoads()) {
    699    return true;
    700  }
    701 
    702  if (!mDocument) {
    703    return false;
    704  }
    705 
    706  // Check for pending restyles or reflows, as they might cause fonts to
    707  // load as new styles apply and text runs are rebuilt.
    708  PresShell* ps = mDocument->GetPresShell();
    709  if (ps && ps->MightHavePendingFontLoads()) {
    710    return true;
    711  }
    712 
    713  // We defer resolving mReady until the document as fully loaded.
    714  if (!mDocument->DidFireDOMContentLoaded()) {
    715    return true;
    716  }
    717 
    718  // And we also wait for any CSS style sheets to finish loading, as their
    719  // styles might cause new fonts to load.
    720  if (css::Loader* loader = mDocument->GetExistingCSSLoader()) {
    721    if (loader->HasPendingLoads()) {
    722      return true;
    723    }
    724  }
    725 
    726  return false;
    727 }
    728 
    729 // nsIDOMEventListener
    730 
    731 NS_IMETHODIMP
    732 FontFaceSetDocumentImpl::HandleEvent(Event* aEvent) {
    733  nsString type;
    734  aEvent->GetType(type);
    735 
    736  if (!type.EqualsLiteral("DOMContentLoaded")) {
    737    return NS_ERROR_FAILURE;
    738  }
    739 
    740  RemoveDOMContentLoadedListener();
    741  CheckLoadingFinished();
    742 
    743  return NS_OK;
    744 }
    745 
    746 // nsICSSLoaderObserver
    747 
    748 NS_IMETHODIMP
    749 FontFaceSetDocumentImpl::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
    750                                          nsresult aStatus) {
    751  CheckLoadingFinished();
    752  return NS_OK;
    753 }
    754 
    755 void FontFaceSetDocumentImpl::FlushUserFontSet() {
    756  if (mDocument) {
    757    mDocument->FlushUserFontSet();
    758  }
    759 }
    760 
    761 void FontFaceSetDocumentImpl::MarkUserFontSetDirty() {
    762  if (mDocument) {
    763    // Ensure we trigger at least a style flush, that will eventually flush the
    764    // user font set. Otherwise the font loads that that flush may cause could
    765    // never be triggered.
    766    if (PresShell* presShell = mDocument->GetPresShell()) {
    767      presShell->EnsureStyleFlush();
    768    }
    769    mDocument->MarkUserFontSetDirty();
    770  }
    771 }
    772 
    773 #undef LOG_ENABLED
    774 #undef LOG