summaryrefslogtreecommitdiffstats
path: root/layout/style/FontFaceSetImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/FontFaceSetImpl.cpp')
-rw-r--r--layout/style/FontFaceSetImpl.cpp956
1 files changed, 956 insertions, 0 deletions
diff --git a/layout/style/FontFaceSetImpl.cpp b/layout/style/FontFaceSetImpl.cpp
new file mode 100644
index 0000000000..9d572701aa
--- /dev/null
+++ b/layout/style/FontFaceSetImpl.cpp
@@ -0,0 +1,956 @@
+/* -*- 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
+ StyleLockedFontFaceRule* 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