mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			956 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			956 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | 
						|
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "FontFaceSetImpl.h"
 | 
						|
 | 
						|
#include "gfxFontConstants.h"
 | 
						|
#include "gfxFontSrcPrincipal.h"
 | 
						|
#include "gfxFontSrcURI.h"
 | 
						|
#include "gfxFontUtils.h"
 | 
						|
#include "gfxPlatformFontList.h"
 | 
						|
#include "mozilla/css/Loader.h"
 | 
						|
#include "mozilla/dom/CSSFontFaceRule.h"
 | 
						|
#include "mozilla/dom/DocumentInlines.h"
 | 
						|
#include "mozilla/dom/Event.h"
 | 
						|
#include "mozilla/dom/FontFaceImpl.h"
 | 
						|
#include "mozilla/dom/FontFaceSet.h"
 | 
						|
#include "mozilla/dom/FontFaceSetBinding.h"
 | 
						|
#include "mozilla/dom/FontFaceSetLoadEvent.h"
 | 
						|
#include "mozilla/dom/FontFaceSetLoadEventBinding.h"
 | 
						|
#include "mozilla/dom/Promise.h"
 | 
						|
#include "mozilla/dom/WorkerCommon.h"
 | 
						|
#include "mozilla/dom/WorkerRunnable.h"
 | 
						|
#include "mozilla/FontPropertyTypes.h"
 | 
						|
#include "mozilla/AsyncEventDispatcher.h"
 | 
						|
#include "mozilla/BasePrincipal.h"
 | 
						|
#include "mozilla/Logging.h"
 | 
						|
#include "mozilla/Preferences.h"
 | 
						|
#include "mozilla/PresShell.h"
 | 
						|
#include "mozilla/PresShellInlines.h"
 | 
						|
#include "mozilla/ServoBindings.h"
 | 
						|
#include "mozilla/ServoCSSParser.h"
 | 
						|
#include "mozilla/ServoStyleSet.h"
 | 
						|
#include "mozilla/ServoUtils.h"
 | 
						|
#include "mozilla/Sprintf.h"
 | 
						|
#include "mozilla/StaticPrefs_layout.h"
 | 
						|
#include "mozilla/Telemetry.h"
 | 
						|
#include "mozilla/LoadInfo.h"
 | 
						|
#include "nsComponentManagerUtils.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsDeviceContext.h"
 | 
						|
#include "nsFontFaceLoader.h"
 | 
						|
#include "nsIConsoleService.h"
 | 
						|
#include "nsIContentPolicy.h"
 | 
						|
#include "nsIDocShell.h"
 | 
						|
#include "nsILoadContext.h"
 | 
						|
#include "nsIPrincipal.h"
 | 
						|
#include "nsIWebNavigation.h"
 | 
						|
#include "nsNetUtil.h"
 | 
						|
#include "nsIInputStream.h"
 | 
						|
#include "nsLayoutUtils.h"
 | 
						|
#include "nsPresContext.h"
 | 
						|
#include "nsPrintfCString.h"
 | 
						|
#include "nsUTF8Utils.h"
 | 
						|
#include "nsDOMNavigationTiming.h"
 | 
						|
#include "ReferrerInfo.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::css;
 | 
						|
using namespace mozilla::dom;
 | 
						|
 | 
						|
#define LOG(args) \
 | 
						|
  MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
 | 
						|
#define LOG_ENABLED() \
 | 
						|
  MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS0(FontFaceSetImpl)
 | 
						|
 | 
						|
FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner)
 | 
						|
    : mMutex("mozilla::dom::FontFaceSetImpl"),
 | 
						|
      mOwner(aOwner),
 | 
						|
      mStatus(FontFaceSetLoadStatus::Loaded),
 | 
						|
      mNonRuleFacesDirty(false),
 | 
						|
      mHasLoadingFontFaces(false),
 | 
						|
      mHasLoadingFontFacesIsDirty(false),
 | 
						|
      mDelayedLoadCheck(false),
 | 
						|
      mBypassCache(false),
 | 
						|
      mPrivateBrowsing(false) {}
 | 
						|
 | 
						|
FontFaceSetImpl::~FontFaceSetImpl() {
 | 
						|
  // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
 | 
						|
  // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
 | 
						|
  MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
 | 
						|
 | 
						|
  Destroy();
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::DestroyLoaders() {
 | 
						|
  mMutex.AssertCurrentThreadIn();
 | 
						|
  if (mLoaders.IsEmpty()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (NS_IsMainThread()) {
 | 
						|
    for (const auto& key : mLoaders.Keys()) {
 | 
						|
      key->Cancel();
 | 
						|
    }
 | 
						|
    mLoaders.Clear();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  class DestroyLoadersRunnable final : public Runnable {
 | 
						|
   public:
 | 
						|
    explicit DestroyLoadersRunnable(FontFaceSetImpl* aFontFaceSet)
 | 
						|
        : Runnable("FontFaceSetImpl::DestroyLoaders"),
 | 
						|
          mFontFaceSet(aFontFaceSet) {}
 | 
						|
 | 
						|
   protected:
 | 
						|
    ~DestroyLoadersRunnable() override = default;
 | 
						|
 | 
						|
    NS_IMETHOD Run() override {
 | 
						|
      RecursiveMutexAutoLock lock(mFontFaceSet->mMutex);
 | 
						|
      mFontFaceSet->DestroyLoaders();
 | 
						|
      return NS_OK;
 | 
						|
    }
 | 
						|
 | 
						|
    // We need to save a reference to the FontFaceSetImpl because the
 | 
						|
    // loaders contain a non-owning reference to it.
 | 
						|
    RefPtr<FontFaceSetImpl> mFontFaceSet;
 | 
						|
  };
 | 
						|
 | 
						|
  auto runnable = MakeRefPtr<DestroyLoadersRunnable>(this);
 | 
						|
  NS_DispatchToMainThread(runnable);
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::Destroy() {
 | 
						|
  nsTArray<FontFaceRecord> nonRuleFaces;
 | 
						|
  nsRefPtrHashtable<nsCStringHashKey, gfxUserFontFamily> fontFamilies;
 | 
						|
 | 
						|
  {
 | 
						|
    RecursiveMutexAutoLock lock(mMutex);
 | 
						|
    DestroyLoaders();
 | 
						|
    nonRuleFaces = std::move(mNonRuleFaces);
 | 
						|
    fontFamilies = std::move(mFontFamilies);
 | 
						|
    mOwner = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList()) {
 | 
						|
    fp->RemoveUserFontSet(this);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::ParseFontShorthandForMatching(
 | 
						|
    const nsACString& aFont, StyleFontFamilyList& aFamilyList,
 | 
						|
    FontWeight& aWeight, FontStretch& aStretch, FontSlantStyle& aStyle,
 | 
						|
    ErrorResult& aRv) {
 | 
						|
  RefPtr<URLExtraData> url = GetURLExtraData();
 | 
						|
  if (!url) {
 | 
						|
    aRv.ThrowInvalidStateError("Missing URLExtraData");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ServoCSSParser::ParseFontShorthandForMatching(
 | 
						|
          aFont, url, aFamilyList, aStyle, aStretch, aWeight)) {
 | 
						|
    aRv.ThrowSyntaxError("Invalid font shorthand");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry,
 | 
						|
                                          const nsAString& aInput) {
 | 
						|
  const char16_t* p = aInput.Data();
 | 
						|
  const char16_t* end = p + aInput.Length();
 | 
						|
 | 
						|
  while (p < end) {
 | 
						|
    uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
 | 
						|
    if (aEntry->CharacterInUnicodeRange(c)) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont,
 | 
						|
                                            const nsAString& aText,
 | 
						|
                                            nsTArray<FontFace*>& aFontFaces,
 | 
						|
                                            ErrorResult& aRv) {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
 | 
						|
  StyleFontFamilyList familyList;
 | 
						|
  FontWeight weight;
 | 
						|
  FontStretch stretch;
 | 
						|
  FontSlantStyle italicStyle;
 | 
						|
  ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
 | 
						|
                                aRv);
 | 
						|
  if (aRv.Failed()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  gfxFontStyle style;
 | 
						|
  style.style = italicStyle;
 | 
						|
  style.weight = weight;
 | 
						|
  style.stretch = stretch;
 | 
						|
 | 
						|
  // Set of FontFaces that we want to return.
 | 
						|
  nsTHashSet<FontFace*> matchingFaces;
 | 
						|
 | 
						|
  for (const StyleSingleFontFamily& fontFamilyName : familyList.list.AsSpan()) {
 | 
						|
    if (!fontFamilyName.IsFamilyName()) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    const auto& name = fontFamilyName.AsFamilyName();
 | 
						|
    RefPtr<gfxFontFamily> family =
 | 
						|
        LookupFamily(nsAtomCString(name.name.AsAtom()));
 | 
						|
 | 
						|
    if (!family) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    AutoTArray<gfxFontEntry*, 4> entries;
 | 
						|
    family->FindAllFontsForStyle(style, entries);
 | 
						|
 | 
						|
    for (gfxFontEntry* e : entries) {
 | 
						|
      FontFaceImpl::Entry* entry = static_cast<FontFaceImpl::Entry*>(e);
 | 
						|
      if (HasAnyCharacterInUnicodeRange(entry, aText)) {
 | 
						|
        entry->FindFontFaceOwners(matchingFaces);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (matchingFaces.IsEmpty()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Add all FontFaces in matchingFaces to aFontFaces, in the order
 | 
						|
  // they appear in the FontFaceSet.
 | 
						|
  FindMatchingFontFaces(matchingFaces, aFontFaces);
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::FindMatchingFontFaces(
 | 
						|
    const nsTHashSet<FontFace*>& aMatchingFaces,
 | 
						|
    nsTArray<FontFace*>& aFontFaces) {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  for (FontFaceRecord& record : mNonRuleFaces) {
 | 
						|
    FontFace* owner = record.mFontFace->GetOwner();
 | 
						|
    if (owner && aMatchingFaces.Contains(owner)) {
 | 
						|
      aFontFaces.AppendElement(owner);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool FontFaceSetImpl::ReadyPromiseIsPending() const {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  return mOwner && mOwner->ReadyPromiseIsPending();
 | 
						|
}
 | 
						|
 | 
						|
FontFaceSetLoadStatus FontFaceSetImpl::Status() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  FlushUserFontSet();
 | 
						|
  return mStatus;
 | 
						|
}
 | 
						|
 | 
						|
bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  FlushUserFontSet();
 | 
						|
 | 
						|
  if (aFontFace->IsInFontFaceSet(this)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aFontFace->HasRule()) {
 | 
						|
    aRv.ThrowInvalidModificationError(
 | 
						|
        "Can't add face to FontFaceSet that comes from an @font-face rule");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  aFontFace->AddFontFaceSet(this);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  for (const FontFaceRecord& rec : mNonRuleFaces) {
 | 
						|
    MOZ_ASSERT(rec.mFontFace != aFontFace,
 | 
						|
               "FontFace should not occur in mNonRuleFaces twice");
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  FontFaceRecord* rec = mNonRuleFaces.AppendElement();
 | 
						|
  rec->mFontFace = aFontFace;
 | 
						|
  rec->mOrigin = Nothing();
 | 
						|
 | 
						|
  mNonRuleFacesDirty = true;
 | 
						|
  MarkUserFontSetDirty();
 | 
						|
  mHasLoadingFontFacesIsDirty = true;
 | 
						|
  CheckLoadingStarted();
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::Clear() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  FlushUserFontSet();
 | 
						|
 | 
						|
  if (mNonRuleFaces.IsEmpty()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
 | 
						|
    FontFaceImpl* f = mNonRuleFaces[i].mFontFace;
 | 
						|
    f->RemoveFontFaceSet(this);
 | 
						|
  }
 | 
						|
 | 
						|
  mNonRuleFaces.Clear();
 | 
						|
  mNonRuleFacesDirty = true;
 | 
						|
  MarkUserFontSetDirty();
 | 
						|
  mHasLoadingFontFacesIsDirty = true;
 | 
						|
  CheckLoadingFinished();
 | 
						|
}
 | 
						|
 | 
						|
bool FontFaceSetImpl::Delete(FontFaceImpl* aFontFace) {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  FlushUserFontSet();
 | 
						|
 | 
						|
  if (aFontFace->HasRule()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  bool removed = false;
 | 
						|
  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
 | 
						|
    if (mNonRuleFaces[i].mFontFace == aFontFace) {
 | 
						|
      mNonRuleFaces.RemoveElementAt(i);
 | 
						|
      removed = true;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (!removed) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  aFontFace->RemoveFontFaceSet(this);
 | 
						|
 | 
						|
  mNonRuleFacesDirty = true;
 | 
						|
  MarkUserFontSetDirty();
 | 
						|
  mHasLoadingFontFacesIsDirty = true;
 | 
						|
  CheckLoadingFinished();
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool FontFaceSetImpl::HasAvailableFontFace(FontFaceImpl* aFontFace) {
 | 
						|
  return aFontFace->IsInFontFaceSet(this);
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader* aLoader) {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  mLoaders.RemoveEntry(aLoader);
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace,
 | 
						|
                                            bool& aFontSetModified) {
 | 
						|
  gfxUserFontAttributes attr;
 | 
						|
  if (!aFontFace->GetAttributes(attr)) {
 | 
						|
    // If there is no family name, this rule cannot contribute a
 | 
						|
    // usable font, so there is no point in processing it further.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoCString family(attr.mFamilyName);
 | 
						|
 | 
						|
  // Just create a new font entry if we haven't got one already.
 | 
						|
  if (!aFontFace->GetUserFontEntry()) {
 | 
						|
    // XXX Should we be checking mLocalRulesUsed like InsertRuleFontFace does?
 | 
						|
    RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace(
 | 
						|
        aFontFace, std::move(attr), StyleOrigin::Author);
 | 
						|
    if (!entry) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    aFontFace->SetUserFontEntry(entry);
 | 
						|
  }
 | 
						|
 | 
						|
  aFontSetModified = true;
 | 
						|
  AddUserFontEntry(family, aFontFace->GetUserFontEntry());
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::UpdateUserFontEntry(gfxUserFontEntry* aEntry,
 | 
						|
                                          gfxUserFontAttributes&& aAttr) {
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
 | 
						|
  bool resetFamilyName = !aEntry->mFamilyName.IsEmpty() &&
 | 
						|
                         aEntry->mFamilyName != aAttr.mFamilyName;
 | 
						|
  // aFontFace already has a user font entry, so we update its attributes
 | 
						|
  // rather than creating a new one.
 | 
						|
  aEntry->UpdateAttributes(std::move(aAttr));
 | 
						|
  // If the family name has changed, remove the entry from its current family
 | 
						|
  // and clear the mFamilyName field so it can be reset when added to a new
 | 
						|
  // family.
 | 
						|
  if (resetFamilyName) {
 | 
						|
    RefPtr<gfxUserFontFamily> family = LookupFamily(aEntry->mFamilyName);
 | 
						|
    if (family) {
 | 
						|
      family->RemoveFontEntry(aEntry);
 | 
						|
    }
 | 
						|
    aEntry->mFamilyName.Truncate(0);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class FontFaceSetImpl::UpdateUserFontEntryRunnable final
 | 
						|
    : public WorkerMainThreadRunnable {
 | 
						|
 public:
 | 
						|
  UpdateUserFontEntryRunnable(FontFaceSetImpl* aSet, gfxUserFontEntry* aEntry,
 | 
						|
                              gfxUserFontAttributes& aAttr)
 | 
						|
      : WorkerMainThreadRunnable(
 | 
						|
            GetCurrentThreadWorkerPrivate(),
 | 
						|
            "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns),
 | 
						|
        mSet(aSet),
 | 
						|
        mEntry(aEntry),
 | 
						|
        mAttr(aAttr) {}
 | 
						|
 | 
						|
  bool MainThreadRun() override {
 | 
						|
    mSet->UpdateUserFontEntry(mEntry, std::move(mAttr));
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  FontFaceSetImpl* mSet;
 | 
						|
  gfxUserFontEntry* mEntry;
 | 
						|
  gfxUserFontAttributes& mAttr;
 | 
						|
};
 | 
						|
 | 
						|
// TODO(emilio): Should this take an nsAtom* aFamilyName instead?
 | 
						|
//
 | 
						|
// All callers have one handy.
 | 
						|
/* static */
 | 
						|
already_AddRefed<gfxUserFontEntry>
 | 
						|
FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace(
 | 
						|
    FontFaceImpl* aFontFace, gfxUserFontAttributes&& aAttr,
 | 
						|
    StyleOrigin aOrigin) {
 | 
						|
  FontFaceSetImpl* set = aFontFace->GetPrimaryFontFaceSet();
 | 
						|
 | 
						|
  RefPtr<gfxUserFontEntry> existingEntry = aFontFace->GetUserFontEntry();
 | 
						|
  if (existingEntry) {
 | 
						|
    if (NS_IsMainThread()) {
 | 
						|
      set->UpdateUserFontEntry(existingEntry, std::move(aAttr));
 | 
						|
    } else {
 | 
						|
      auto task =
 | 
						|
          MakeRefPtr<UpdateUserFontEntryRunnable>(set, existingEntry, aAttr);
 | 
						|
      IgnoredErrorResult ignoredRv;
 | 
						|
      task->Dispatch(Canceling, ignoredRv);
 | 
						|
    }
 | 
						|
    return existingEntry.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  // set up src array
 | 
						|
  nsTArray<gfxFontFaceSrc> srcArray;
 | 
						|
 | 
						|
  if (aFontFace->HasFontData()) {
 | 
						|
    gfxFontFaceSrc* face = srcArray.AppendElement();
 | 
						|
    if (!face) {
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
    face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
 | 
						|
    face->mBuffer = aFontFace->TakeBufferSource();
 | 
						|
  } else {
 | 
						|
    size_t len = aAttr.mSources.Length();
 | 
						|
    for (size_t i = 0; i < len; ++i) {
 | 
						|
      gfxFontFaceSrc* face = srcArray.AppendElement();
 | 
						|
      const auto& component = aAttr.mSources[i];
 | 
						|
      switch (component.tag) {
 | 
						|
        case StyleFontFaceSourceListComponent::Tag::Local: {
 | 
						|
          nsAtom* atom = component.AsLocal();
 | 
						|
          face->mLocalName.Append(nsAtomCString(atom));
 | 
						|
          face->mSourceType = gfxFontFaceSrc::eSourceType_Local;
 | 
						|
          face->mURI = nullptr;
 | 
						|
          face->mFormatHint = StyleFontFaceSourceFormatKeyword::None;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case StyleFontFaceSourceListComponent::Tag::Url: {
 | 
						|
          face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
 | 
						|
          const StyleCssUrl* url = component.AsUrl();
 | 
						|
          nsIURI* uri = url->GetURI();
 | 
						|
          face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr;
 | 
						|
          const URLExtraData& extraData = url->ExtraData();
 | 
						|
          face->mReferrerInfo = extraData.ReferrerInfo();
 | 
						|
 | 
						|
          // agent and user stylesheets are treated slightly differently,
 | 
						|
          // the same-site origin check and access control headers are
 | 
						|
          // enforced against the sheet principal rather than the document
 | 
						|
          // principal to allow user stylesheets to include @font-face rules
 | 
						|
          if (aOrigin == StyleOrigin::User ||
 | 
						|
              aOrigin == StyleOrigin::UserAgent) {
 | 
						|
            face->mUseOriginPrincipal = true;
 | 
						|
            face->mOriginPrincipal = new gfxFontSrcPrincipal(
 | 
						|
                extraData.Principal(), extraData.Principal());
 | 
						|
          }
 | 
						|
 | 
						|
          face->mLocalName.Truncate();
 | 
						|
          face->mFormatHint = StyleFontFaceSourceFormatKeyword::None;
 | 
						|
          face->mTechFlags = StyleFontFaceSourceTechFlags::Empty();
 | 
						|
 | 
						|
          if (i + 1 < len) {
 | 
						|
            // Check for a format hint.
 | 
						|
            const auto& next = aAttr.mSources[i + 1];
 | 
						|
            switch (next.tag) {
 | 
						|
              case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
 | 
						|
                face->mFormatHint = next.format_hint_keyword._0;
 | 
						|
                i++;
 | 
						|
                break;
 | 
						|
              case StyleFontFaceSourceListComponent::Tag::FormatHintString: {
 | 
						|
                nsDependentCSubstring valueString(
 | 
						|
                    reinterpret_cast<const char*>(
 | 
						|
                        next.format_hint_string.utf8_bytes),
 | 
						|
                    next.format_hint_string.length);
 | 
						|
 | 
						|
                if (valueString.LowerCaseEqualsASCII("woff")) {
 | 
						|
                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff;
 | 
						|
                } else if (valueString.LowerCaseEqualsASCII("woff2")) {
 | 
						|
                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2;
 | 
						|
                } else if (valueString.LowerCaseEqualsASCII("opentype")) {
 | 
						|
                  face->mFormatHint =
 | 
						|
                      StyleFontFaceSourceFormatKeyword::Opentype;
 | 
						|
                } else if (valueString.LowerCaseEqualsASCII("truetype")) {
 | 
						|
                  face->mFormatHint =
 | 
						|
                      StyleFontFaceSourceFormatKeyword::Truetype;
 | 
						|
                } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
 | 
						|
                  face->mFormatHint =
 | 
						|
                      StyleFontFaceSourceFormatKeyword::Truetype;
 | 
						|
                } else if (valueString.LowerCaseEqualsASCII(
 | 
						|
                               "embedded-opentype")) {
 | 
						|
                  face->mFormatHint =
 | 
						|
                      StyleFontFaceSourceFormatKeyword::EmbeddedOpentype;
 | 
						|
                } else if (valueString.LowerCaseEqualsASCII("svg")) {
 | 
						|
                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Svg;
 | 
						|
                } else if (StaticPrefs::layout_css_font_variations_enabled()) {
 | 
						|
                  // Non-standard values that Firefox accepted, for back-compat;
 | 
						|
                  // these are superseded by the tech() function.
 | 
						|
                  if (valueString.LowerCaseEqualsASCII("woff-variations")) {
 | 
						|
                    face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff;
 | 
						|
                  } else if (valueString.LowerCaseEqualsASCII(
 | 
						|
                                 "woff2-variations")) {
 | 
						|
                    face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2;
 | 
						|
                  } else if (valueString.LowerCaseEqualsASCII(
 | 
						|
                                 "opentype-variations")) {
 | 
						|
                    face->mFormatHint =
 | 
						|
                        StyleFontFaceSourceFormatKeyword::Opentype;
 | 
						|
                  } else if (valueString.LowerCaseEqualsASCII(
 | 
						|
                                 "truetype-variations")) {
 | 
						|
                    face->mFormatHint =
 | 
						|
                        StyleFontFaceSourceFormatKeyword::Truetype;
 | 
						|
                  } else {
 | 
						|
                    face->mFormatHint =
 | 
						|
                        StyleFontFaceSourceFormatKeyword::Unknown;
 | 
						|
                  }
 | 
						|
                } else {
 | 
						|
                  // unknown format specified, mark to distinguish from the
 | 
						|
                  // case where no format hints are specified
 | 
						|
                  face->mFormatHint = StyleFontFaceSourceFormatKeyword::Unknown;
 | 
						|
                }
 | 
						|
                i++;
 | 
						|
                break;
 | 
						|
              }
 | 
						|
              case StyleFontFaceSourceListComponent::Tag::TechFlags:
 | 
						|
              case StyleFontFaceSourceListComponent::Tag::Local:
 | 
						|
              case StyleFontFaceSourceListComponent::Tag::Url:
 | 
						|
                break;
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          if (i + 1 < len) {
 | 
						|
            // Check for a set of font-technologies flags.
 | 
						|
            const auto& next = aAttr.mSources[i + 1];
 | 
						|
            if (next.IsTechFlags()) {
 | 
						|
              face->mTechFlags = next.AsTechFlags();
 | 
						|
              i++;
 | 
						|
            }
 | 
						|
          }
 | 
						|
 | 
						|
          if (!face->mURI) {
 | 
						|
            // if URI not valid, omit from src array
 | 
						|
            srcArray.RemoveLastElement();
 | 
						|
            NS_WARNING("null url in @font-face rule");
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
 | 
						|
        case StyleFontFaceSourceListComponent::Tag::FormatHintString:
 | 
						|
        case StyleFontFaceSourceListComponent::Tag::TechFlags:
 | 
						|
          MOZ_ASSERT_UNREACHABLE(
 | 
						|
              "Should always come after a URL source, and be consumed already");
 | 
						|
          break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (srcArray.IsEmpty()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  return set->FindOrCreateUserFontEntry(std::move(srcArray), std::move(aAttr));
 | 
						|
}
 | 
						|
 | 
						|
nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry,
 | 
						|
                                     uint32_t aSrcIndex, const char* aMessage,
 | 
						|
                                     uint32_t aFlags, nsresult aStatus) {
 | 
						|
  nsAutoCString familyName;
 | 
						|
  nsAutoCString fontURI;
 | 
						|
  aUserFontEntry->GetFamilyNameAndURIForLogging(aSrcIndex, familyName, fontURI);
 | 
						|
 | 
						|
  nsAutoCString weightString;
 | 
						|
  aUserFontEntry->Weight().ToString(weightString);
 | 
						|
  nsAutoCString stretchString;
 | 
						|
  aUserFontEntry->Stretch().ToString(stretchString);
 | 
						|
  nsPrintfCString message(
 | 
						|
      "downloadable font: %s "
 | 
						|
      "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
 | 
						|
      aMessage, familyName.get(),
 | 
						|
      aUserFontEntry->IsItalic() ? "italic" : "normal",  // XXX todo: oblique?
 | 
						|
      weightString.get(), stretchString.get(), aSrcIndex);
 | 
						|
 | 
						|
  if (NS_FAILED(aStatus)) {
 | 
						|
    message.AppendLiteral(": ");
 | 
						|
    switch (aStatus) {
 | 
						|
      case NS_ERROR_DOM_BAD_URI:
 | 
						|
        message.AppendLiteral("bad URI or cross-site access not allowed");
 | 
						|
        break;
 | 
						|
      case NS_ERROR_CONTENT_BLOCKED:
 | 
						|
        message.AppendLiteral("content blocked");
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        message.AppendLiteral("status=");
 | 
						|
        message.AppendInt(static_cast<uint32_t>(aStatus));
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  message.AppendLiteral(" source: ");
 | 
						|
  message.Append(fontURI);
 | 
						|
 | 
						|
  LOG(("userfonts (%p) %s", this, message.get()));
 | 
						|
 | 
						|
  if (GetCurrentThreadWorkerPrivate()) {
 | 
						|
    // TODO(aosmond): Log to the console for workers. See bug 1778537.
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIConsoleService> console(
 | 
						|
      do_GetService(NS_CONSOLESERVICE_CONTRACTID));
 | 
						|
  if (!console) {
 | 
						|
    return NS_ERROR_NOT_AVAILABLE;
 | 
						|
  }
 | 
						|
 | 
						|
  // try to give the user an indication of where the rule came from
 | 
						|
  RawServoFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
 | 
						|
  nsString href;
 | 
						|
  nsAutoCString text;
 | 
						|
  uint32_t line = 0;
 | 
						|
  uint32_t column = 0;
 | 
						|
  if (rule) {
 | 
						|
    Servo_FontFaceRule_GetCssText(rule, &text);
 | 
						|
    Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
 | 
						|
    // FIXME We need to figure out an approach to get the style sheet
 | 
						|
    // of this raw rule. See bug 1450903.
 | 
						|
#if 0
 | 
						|
    StyleSheet* sheet = rule->GetStyleSheet();
 | 
						|
    // if the style sheet is removed while the font is loading can be null
 | 
						|
    if (sheet) {
 | 
						|
      nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
 | 
						|
      CopyUTF8toUTF16(spec, href);
 | 
						|
    } else {
 | 
						|
      NS_WARNING("null parent stylesheet for @font-face rule");
 | 
						|
      href.AssignLiteral("unknown");
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    // Leave href empty if we don't know how to get the correct sheet.
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult rv;
 | 
						|
  nsCOMPtr<nsIScriptError> scriptError =
 | 
						|
      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
 | 
						|
                                     href,                         // file
 | 
						|
                                     NS_ConvertUTF8toUTF16(text),  // src line
 | 
						|
                                     line, column,
 | 
						|
                                     aFlags,        // flags
 | 
						|
                                     "CSS Loader",  // category (make separate?)
 | 
						|
                                     GetInnerWindowID());
 | 
						|
  if (NS_SUCCEEDED(rv)) {
 | 
						|
    console->LogMessage(scriptError);
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
 | 
						|
                                           const gfxFontFaceSrc* aFontFaceSrc,
 | 
						|
                                           uint8_t*& aBuffer,
 | 
						|
                                           uint32_t& aBufferLength) {
 | 
						|
  nsCOMPtr<nsIChannel> channel;
 | 
						|
  nsresult rv = CreateChannelForSyncLoadFontData(getter_AddRefs(channel),
 | 
						|
                                                 aFontToLoad, aFontFaceSrc);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  // blocking stream is OK for data URIs
 | 
						|
  nsCOMPtr<nsIInputStream> stream;
 | 
						|
  rv = channel->Open(getter_AddRefs(stream));
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  uint64_t bufferLength64;
 | 
						|
  rv = stream->Available(&bufferLength64);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  if (bufferLength64 == 0) {
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
  if (bufferLength64 > UINT32_MAX) {
 | 
						|
    return NS_ERROR_FILE_TOO_BIG;
 | 
						|
  }
 | 
						|
  aBufferLength = static_cast<uint32_t>(bufferLength64);
 | 
						|
 | 
						|
  // read all the decoded data
 | 
						|
  aBuffer = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * aBufferLength));
 | 
						|
  if (!aBuffer) {
 | 
						|
    aBufferLength = 0;
 | 
						|
    return NS_ERROR_OUT_OF_MEMORY;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t numRead, totalRead = 0;
 | 
						|
  while (NS_SUCCEEDED(
 | 
						|
             rv = stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
 | 
						|
                               aBufferLength - totalRead, &numRead)) &&
 | 
						|
         numRead != 0) {
 | 
						|
    totalRead += numRead;
 | 
						|
    if (totalRead > aBufferLength) {
 | 
						|
      rv = NS_ERROR_FAILURE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // make sure there's a mime type
 | 
						|
  if (NS_SUCCEEDED(rv)) {
 | 
						|
    nsAutoCString mimeType;
 | 
						|
    rv = channel->GetContentType(mimeType);
 | 
						|
    aBufferLength = totalRead;
 | 
						|
  }
 | 
						|
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    free(aBuffer);
 | 
						|
    aBuffer = nullptr;
 | 
						|
    aBufferLength = 0;
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::OnFontFaceStatusChanged(FontFaceImpl* aFontFace) {
 | 
						|
  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  MOZ_ASSERT(HasAvailableFontFace(aFontFace));
 | 
						|
 | 
						|
  mHasLoadingFontFacesIsDirty = true;
 | 
						|
 | 
						|
  if (aFontFace->Status() == FontFaceLoadStatus::Loading) {
 | 
						|
    CheckLoadingStarted();
 | 
						|
  } else {
 | 
						|
    MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded ||
 | 
						|
               aFontFace->Status() == FontFaceLoadStatus::Error);
 | 
						|
    // When a font finishes downloading, nsPresContext::UserFontSetUpdated
 | 
						|
    // will be called immediately afterwards to request a reflow of the
 | 
						|
    // relevant elements in the document.  We want to wait until the reflow
 | 
						|
    // request has been done before the FontFaceSet is marked as Loaded so
 | 
						|
    // that we don't briefly set the FontFaceSet to Loaded and then Loading
 | 
						|
    // again once the reflow is pending.  So we go around the event loop
 | 
						|
    // and call CheckLoadingFinished() after the reflow has been queued.
 | 
						|
    if (!mDelayedLoadCheck) {
 | 
						|
      mDelayedLoadCheck = true;
 | 
						|
      DispatchCheckLoadingFinishedAfterDelay();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() {
 | 
						|
  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
 | 
						|
 | 
						|
  if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) {
 | 
						|
    // See comments in Gecko_GetFontMetrics.
 | 
						|
    //
 | 
						|
    // We can't just dispatch the runnable below if we're not on the main
 | 
						|
    // thread, since it needs to take a strong reference to the FontFaceSet,
 | 
						|
    // and being a DOM object, FontFaceSet doesn't support thread-safe
 | 
						|
    // refcounting.
 | 
						|
    set->AppendTask(
 | 
						|
        PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(
 | 
						|
            this));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  DispatchToOwningThread(
 | 
						|
      "FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay",
 | 
						|
      [self = RefPtr{this}]() { self->CheckLoadingFinishedAfterDelay(); });
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  mDelayedLoadCheck = false;
 | 
						|
  CheckLoadingFinished();
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::CheckLoadingStarted() {
 | 
						|
  gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
 | 
						|
  if (!HasLoadingFontFaces()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mStatus == FontFaceSetLoadStatus::Loading) {
 | 
						|
    // We have already dispatched a loading event and replaced mReady
 | 
						|
    // with a fresh, unresolved promise.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  mStatus = FontFaceSetLoadStatus::Loading;
 | 
						|
 | 
						|
  if (IsOnOwningThread()) {
 | 
						|
    OnLoadingStarted();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted",
 | 
						|
                         [self = RefPtr{this}]() { self->OnLoadingStarted(); });
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::OnLoadingStarted() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  if (mOwner) {
 | 
						|
    mOwner->DispatchLoadingEventAndReplaceReadyPromise();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::UpdateHasLoadingFontFaces() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  mHasLoadingFontFacesIsDirty = false;
 | 
						|
  mHasLoadingFontFaces = false;
 | 
						|
  for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
 | 
						|
    if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) {
 | 
						|
      mHasLoadingFontFaces = true;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool FontFaceSetImpl::HasLoadingFontFaces() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  if (mHasLoadingFontFacesIsDirty) {
 | 
						|
    UpdateHasLoadingFontFaces();
 | 
						|
  }
 | 
						|
  return mHasLoadingFontFaces;
 | 
						|
}
 | 
						|
 | 
						|
bool FontFaceSetImpl::MightHavePendingFontLoads() {
 | 
						|
  // Check for FontFace objects in the FontFaceSet that are still loading.
 | 
						|
  return HasLoadingFontFaces();
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::CheckLoadingFinished() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  if (mDelayedLoadCheck) {
 | 
						|
    // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ReadyPromiseIsPending()) {
 | 
						|
    // We've already resolved mReady (or set the flag to do that lazily) and
 | 
						|
    // dispatched the loadingdone/loadingerror events.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (MightHavePendingFontLoads()) {
 | 
						|
    // We're not finished loading yet.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  mStatus = FontFaceSetLoadStatus::Loaded;
 | 
						|
 | 
						|
  if (IsOnOwningThread()) {
 | 
						|
    OnLoadingFinished();
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  DispatchToOwningThread(
 | 
						|
      "FontFaceSetImpl::CheckLoadingFinished",
 | 
						|
      [self = RefPtr{this}]() { self->OnLoadingFinished(); });
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::OnLoadingFinished() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  if (mOwner) {
 | 
						|
    mOwner->MaybeResolve();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  mAllowedFontLoads.Clear();
 | 
						|
  IncrementGeneration(false);
 | 
						|
}
 | 
						|
 | 
						|
// -- gfxUserFontSet
 | 
						|
// ------------------------------------------------
 | 
						|
 | 
						|
already_AddRefed<gfxFontSrcPrincipal>
 | 
						|
FontFaceSetImpl::GetStandardFontLoadPrincipal() const {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  return RefPtr{mStandardFontLoadPrincipal}.forget();
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize,
 | 
						|
                                         TimeStamp aDoneTime) {
 | 
						|
  mDownloadCount++;
 | 
						|
  mDownloadSize += aFontSize;
 | 
						|
  Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024);
 | 
						|
 | 
						|
  TimeStamp navStart = GetNavigationStartTimeStamp();
 | 
						|
  TimeStamp zero;
 | 
						|
  if (navStart != zero) {
 | 
						|
    Telemetry::AccumulateTimeDelta(Telemetry::WEBFONT_DOWNLOAD_TIME_AFTER_START,
 | 
						|
                                   navStart, aDoneTime);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::DoRebuildUserFontSet() { MarkUserFontSetDirty(); }
 | 
						|
 | 
						|
already_AddRefed<gfxUserFontEntry> FontFaceSetImpl::CreateUserFontEntry(
 | 
						|
    nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList,
 | 
						|
    gfxUserFontAttributes&& aAttr) {
 | 
						|
  RefPtr<gfxUserFontEntry> entry = new FontFaceImpl::Entry(
 | 
						|
      this, std::move(aFontFaceSrcList), std::move(aAttr));
 | 
						|
  return entry.forget();
 | 
						|
}
 | 
						|
 | 
						|
void FontFaceSetImpl::ForgetLocalFaces() {
 | 
						|
  // We cannot hold our lock at the same time as the gfxUserFontFamily lock, so
 | 
						|
  // we need to make a copy of the table first.
 | 
						|
  nsTArray<RefPtr<gfxUserFontFamily>> fontFamilies;
 | 
						|
  {
 | 
						|
    RecursiveMutexAutoLock lock(mMutex);
 | 
						|
    fontFamilies.SetCapacity(mFontFamilies.Count());
 | 
						|
    for (const auto& fam : mFontFamilies.Values()) {
 | 
						|
      fontFamilies.AppendElement(fam);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (const auto& fam : fontFamilies) {
 | 
						|
    ForgetLocalFace(fam);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<gfxUserFontFamily> FontFaceSetImpl::GetFamily(
 | 
						|
    const nsACString& aFamilyName) {
 | 
						|
  RecursiveMutexAutoLock lock(mMutex);
 | 
						|
  return gfxUserFontSet::GetFamily(aFamilyName);
 | 
						|
}
 | 
						|
 | 
						|
#undef LOG_ENABLED
 | 
						|
#undef LOG
 |