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/. */
7 #include "FontFaceSetImpl.h"
9 #include "gfxFontConstants.h"
10 #include "gfxFontSrcPrincipal.h"
11 #include "gfxFontSrcURI.h"
12 #include "gfxFontUtils.h"
13 #include "gfxPlatformFontList.h"
14 #include "mozilla/css/Loader.h"
15 #include "mozilla/dom/CSSFontFaceRule.h"
16 #include "mozilla/dom/DocumentInlines.h"
17 #include "mozilla/dom/Event.h"
18 #include "mozilla/dom/FontFaceImpl.h"
19 #include "mozilla/dom/FontFaceSet.h"
20 #include "mozilla/dom/FontFaceSetBinding.h"
21 #include "mozilla/dom/FontFaceSetLoadEvent.h"
22 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
23 #include "mozilla/dom/Promise.h"
24 #include "mozilla/dom/WorkerCommon.h"
25 #include "mozilla/dom/WorkerRunnable.h"
26 #include "mozilla/FontPropertyTypes.h"
27 #include "mozilla/AsyncEventDispatcher.h"
28 #include "mozilla/BasePrincipal.h"
29 #include "mozilla/glean/GleanMetrics.h"
30 #include "mozilla/Logging.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/PresShell.h"
33 #include "mozilla/PresShellInlines.h"
34 #include "mozilla/ServoBindings.h"
35 #include "mozilla/ServoCSSParser.h"
36 #include "mozilla/ServoStyleSet.h"
37 #include "mozilla/ServoUtils.h"
38 #include "mozilla/Sprintf.h"
39 #include "mozilla/StaticPrefs_layout.h"
40 #include "mozilla/Telemetry.h"
41 #include "mozilla/LoadInfo.h"
42 #include "nsComponentManagerUtils.h"
43 #include "nsContentUtils.h"
44 #include "nsDeviceContext.h"
45 #include "nsFontFaceLoader.h"
46 #include "nsIConsoleService.h"
47 #include "nsIContentPolicy.h"
48 #include "nsIDocShell.h"
49 #include "nsILoadContext.h"
50 #include "nsIPrincipal.h"
51 #include "nsIWebNavigation.h"
52 #include "nsNetUtil.h"
53 #include "nsIInputStream.h"
54 #include "nsLayoutUtils.h"
55 #include "nsPresContext.h"
56 #include "nsPrintfCString.h"
57 #include "nsUTF8Utils.h"
58 #include "nsDOMNavigationTiming.h"
59 #include "ReferrerInfo.h"
61 using namespace mozilla
;
62 using namespace mozilla::css
;
63 using namespace mozilla::dom
;
66 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
67 #define LOG_ENABLED() \
68 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
70 NS_IMPL_ISUPPORTS0(FontFaceSetImpl
)
72 FontFaceSetImpl::FontFaceSetImpl(FontFaceSet
* aOwner
)
73 : mMutex("mozilla::dom::FontFaceSetImpl"),
75 mStatus(FontFaceSetLoadStatus::Loaded
),
76 mNonRuleFacesDirty(false),
77 mHasLoadingFontFaces(false),
78 mHasLoadingFontFacesIsDirty(false),
79 mDelayedLoadCheck(false),
81 mPrivateBrowsing(false) {}
83 FontFaceSetImpl::~FontFaceSetImpl() {
84 // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
85 // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
86 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
91 void FontFaceSetImpl::DestroyLoaders() {
92 mMutex
.AssertCurrentThreadIn();
93 if (mLoaders
.IsEmpty()) {
96 if (NS_IsMainThread()) {
97 for (const auto& key
: mLoaders
.Keys()) {
104 class DestroyLoadersRunnable final
: public Runnable
{
106 explicit DestroyLoadersRunnable(FontFaceSetImpl
* aFontFaceSet
)
107 : Runnable("FontFaceSetImpl::DestroyLoaders"),
108 mFontFaceSet(aFontFaceSet
) {}
111 ~DestroyLoadersRunnable() override
= default;
113 NS_IMETHOD
Run() override
{
114 RecursiveMutexAutoLock
lock(mFontFaceSet
->mMutex
);
115 mFontFaceSet
->DestroyLoaders();
119 // We need to save a reference to the FontFaceSetImpl because the
120 // loaders contain a non-owning reference to it.
121 RefPtr
<FontFaceSetImpl
> mFontFaceSet
;
124 auto runnable
= MakeRefPtr
<DestroyLoadersRunnable
>(this);
125 NS_DispatchToMainThread(runnable
);
128 void FontFaceSetImpl::Destroy() {
129 nsTArray
<FontFaceRecord
> nonRuleFaces
;
130 nsRefPtrHashtable
<nsCStringHashKey
, gfxUserFontFamily
> fontFamilies
;
133 RecursiveMutexAutoLock
lock(mMutex
);
135 nonRuleFaces
= std::move(mNonRuleFaces
);
136 fontFamilies
= std::move(mFontFamilies
);
140 if (gfxPlatformFontList
* fp
= gfxPlatformFontList::PlatformFontList()) {
141 fp
->RemoveUserFontSet(this);
145 void FontFaceSetImpl::ParseFontShorthandForMatching(
146 const nsACString
& aFont
, StyleFontFamilyList
& aFamilyList
,
147 FontWeight
& aWeight
, FontStretch
& aStretch
, FontSlantStyle
& aStyle
,
149 RefPtr
<URLExtraData
> url
= GetURLExtraData();
151 aRv
.ThrowInvalidStateError("Missing URLExtraData");
155 if (!ServoCSSParser::ParseFontShorthandForMatching(
156 aFont
, url
, aFamilyList
, aStyle
, aStretch
, aWeight
)) {
157 aRv
.ThrowSyntaxError("Invalid font shorthand");
162 static bool HasAnyCharacterInUnicodeRange(gfxUserFontEntry
* aEntry
,
163 const nsAString
& aInput
) {
164 const char16_t
* p
= aInput
.Data();
165 const char16_t
* end
= p
+ aInput
.Length();
168 uint32_t c
= UTF16CharEnumerator::NextChar(&p
, end
);
169 if (aEntry
->CharacterInUnicodeRange(c
)) {
176 void FontFaceSetImpl::FindMatchingFontFaces(const nsACString
& aFont
,
177 const nsAString
& aText
,
178 nsTArray
<FontFace
*>& aFontFaces
,
180 RecursiveMutexAutoLock
lock(mMutex
);
182 StyleFontFamilyList familyList
;
185 FontSlantStyle italicStyle
;
186 ParseFontShorthandForMatching(aFont
, familyList
, weight
, stretch
, italicStyle
,
193 style
.style
= italicStyle
;
194 style
.weight
= weight
;
195 style
.stretch
= stretch
;
197 // Set of FontFaces that we want to return.
198 nsTHashSet
<FontFace
*> matchingFaces
;
200 for (const StyleSingleFontFamily
& fontFamilyName
: familyList
.list
.AsSpan()) {
201 if (!fontFamilyName
.IsFamilyName()) {
205 const auto& name
= fontFamilyName
.AsFamilyName();
206 RefPtr
<gfxFontFamily
> family
=
207 LookupFamily(nsAtomCString(name
.name
.AsAtom()));
213 AutoTArray
<gfxFontEntry
*, 4> entries
;
214 family
->FindAllFontsForStyle(style
, entries
);
216 for (gfxFontEntry
* e
: entries
) {
217 FontFaceImpl::Entry
* entry
= static_cast<FontFaceImpl::Entry
*>(e
);
218 if (HasAnyCharacterInUnicodeRange(entry
, aText
)) {
219 entry
->FindFontFaceOwners(matchingFaces
);
224 if (matchingFaces
.IsEmpty()) {
228 // Add all FontFaces in matchingFaces to aFontFaces, in the order
229 // they appear in the FontFaceSet.
230 FindMatchingFontFaces(matchingFaces
, aFontFaces
);
233 void FontFaceSetImpl::FindMatchingFontFaces(
234 const nsTHashSet
<FontFace
*>& aMatchingFaces
,
235 nsTArray
<FontFace
*>& aFontFaces
) {
236 RecursiveMutexAutoLock
lock(mMutex
);
237 for (FontFaceRecord
& record
: mNonRuleFaces
) {
238 FontFace
* owner
= record
.mFontFace
->GetOwner();
239 if (owner
&& aMatchingFaces
.Contains(owner
)) {
240 aFontFaces
.AppendElement(owner
);
245 bool FontFaceSetImpl::ReadyPromiseIsPending() const {
246 RecursiveMutexAutoLock
lock(mMutex
);
247 return mOwner
&& mOwner
->ReadyPromiseIsPending();
250 FontFaceSetLoadStatus
FontFaceSetImpl::Status() {
251 RecursiveMutexAutoLock
lock(mMutex
);
256 bool FontFaceSetImpl::Add(FontFaceImpl
* aFontFace
, ErrorResult
& aRv
) {
257 RecursiveMutexAutoLock
lock(mMutex
);
260 if (aFontFace
->IsInFontFaceSet(this)) {
264 if (aFontFace
->HasRule()) {
265 aRv
.ThrowInvalidModificationError(
266 "Can't add face to FontFaceSet that comes from an @font-face rule");
270 aFontFace
->AddFontFaceSet(this);
273 for (const FontFaceRecord
& rec
: mNonRuleFaces
) {
274 MOZ_ASSERT(rec
.mFontFace
!= aFontFace
,
275 "FontFace should not occur in mNonRuleFaces twice");
279 FontFaceRecord
* rec
= mNonRuleFaces
.AppendElement();
280 rec
->mFontFace
= aFontFace
;
281 rec
->mOrigin
= Nothing();
283 mNonRuleFacesDirty
= true;
284 MarkUserFontSetDirty();
285 mHasLoadingFontFacesIsDirty
= true;
286 CheckLoadingStarted();
290 void FontFaceSetImpl::Clear() {
291 RecursiveMutexAutoLock
lock(mMutex
);
294 if (mNonRuleFaces
.IsEmpty()) {
298 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
299 FontFaceImpl
* f
= mNonRuleFaces
[i
].mFontFace
;
300 f
->RemoveFontFaceSet(this);
303 mNonRuleFaces
.Clear();
304 mNonRuleFacesDirty
= true;
305 MarkUserFontSetDirty();
306 mHasLoadingFontFacesIsDirty
= true;
307 CheckLoadingFinished();
310 bool FontFaceSetImpl::Delete(FontFaceImpl
* aFontFace
) {
311 RecursiveMutexAutoLock
lock(mMutex
);
314 if (aFontFace
->HasRule()) {
318 bool removed
= false;
319 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
320 if (mNonRuleFaces
[i
].mFontFace
== aFontFace
) {
321 mNonRuleFaces
.RemoveElementAt(i
);
330 aFontFace
->RemoveFontFaceSet(this);
332 mNonRuleFacesDirty
= true;
333 MarkUserFontSetDirty();
334 mHasLoadingFontFacesIsDirty
= true;
335 CheckLoadingFinished();
339 bool FontFaceSetImpl::HasAvailableFontFace(FontFaceImpl
* aFontFace
) {
340 return aFontFace
->IsInFontFaceSet(this);
343 void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader
* aLoader
) {
344 RecursiveMutexAutoLock
lock(mMutex
);
345 mLoaders
.RemoveEntry(aLoader
);
348 void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl
* aFontFace
) {
349 gfxUserFontAttributes attr
;
350 if (!aFontFace
->GetAttributes(attr
)) {
351 // If there is no family name, this rule cannot contribute a
352 // usable font, so there is no point in processing it further.
356 nsAutoCString
family(attr
.mFamilyName
);
358 // Just create a new font entry if we haven't got one already.
359 if (!aFontFace
->GetUserFontEntry()) {
360 // XXX Should we be checking mLocalRulesUsed like InsertRuleFontFace does?
361 RefPtr
<gfxUserFontEntry
> entry
= FindOrCreateUserFontEntryFromFontFace(
362 aFontFace
, std::move(attr
), StyleOrigin::Author
);
366 aFontFace
->SetUserFontEntry(entry
);
368 AddUserFontEntry(family
, aFontFace
->GetUserFontEntry());
371 void FontFaceSetImpl::UpdateUserFontEntry(gfxUserFontEntry
* aEntry
,
372 gfxUserFontAttributes
&& aAttr
) {
373 MOZ_ASSERT(NS_IsMainThread());
375 bool resetFamilyName
= !aEntry
->mFamilyName
.IsEmpty() &&
376 aEntry
->mFamilyName
!= aAttr
.mFamilyName
;
377 // aFontFace already has a user font entry, so we update its attributes
378 // rather than creating a new one.
379 aEntry
->UpdateAttributes(std::move(aAttr
));
380 // If the family name has changed, remove the entry from its current family
381 // and clear the mFamilyName field so it can be reset when added to a new
383 if (resetFamilyName
) {
384 RefPtr
<gfxUserFontFamily
> family
= LookupFamily(aEntry
->mFamilyName
);
386 family
->RemoveFontEntry(aEntry
);
388 aEntry
->mFamilyName
.Truncate(0);
392 class FontFaceSetImpl::UpdateUserFontEntryRunnable final
393 : public WorkerMainThreadRunnable
{
395 UpdateUserFontEntryRunnable(FontFaceSetImpl
* aSet
, gfxUserFontEntry
* aEntry
,
396 gfxUserFontAttributes
& aAttr
)
397 : WorkerMainThreadRunnable(
398 GetCurrentThreadWorkerPrivate(),
399 "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns
),
404 bool MainThreadRun() override
{
405 mSet
->UpdateUserFontEntry(mEntry
, std::move(mAttr
));
410 FontFaceSetImpl
* mSet
;
411 gfxUserFontEntry
* mEntry
;
412 gfxUserFontAttributes
& mAttr
;
415 // TODO(emilio): Should this take an nsAtom* aFamilyName instead?
417 // All callers have one handy.
419 already_AddRefed
<gfxUserFontEntry
>
420 FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace(
421 FontFaceImpl
* aFontFace
, gfxUserFontAttributes
&& aAttr
,
422 StyleOrigin aOrigin
) {
423 FontFaceSetImpl
* set
= aFontFace
->GetPrimaryFontFaceSet();
425 RefPtr
<gfxUserFontEntry
> existingEntry
= aFontFace
->GetUserFontEntry();
427 if (NS_IsMainThread()) {
428 set
->UpdateUserFontEntry(existingEntry
, std::move(aAttr
));
431 MakeRefPtr
<UpdateUserFontEntryRunnable
>(set
, existingEntry
, aAttr
);
432 IgnoredErrorResult ignoredRv
;
433 task
->Dispatch(Canceling
, ignoredRv
);
435 return existingEntry
.forget();
439 nsTArray
<gfxFontFaceSrc
> srcArray
;
441 if (aFontFace
->HasFontData()) {
442 gfxFontFaceSrc
* face
= srcArray
.AppendElement();
447 face
->mSourceType
= gfxFontFaceSrc::eSourceType_Buffer
;
448 face
->mBuffer
= aFontFace
->TakeBufferSource();
450 size_t len
= aAttr
.mSources
.Length();
451 for (size_t i
= 0; i
< len
; ++i
) {
452 gfxFontFaceSrc
* face
= srcArray
.AppendElement();
453 const auto& component
= aAttr
.mSources
[i
];
454 switch (component
.tag
) {
455 case StyleFontFaceSourceListComponent::Tag::Local
: {
456 nsAtom
* atom
= component
.AsLocal();
457 face
->mLocalName
.Append(nsAtomCString(atom
));
458 face
->mSourceType
= gfxFontFaceSrc::eSourceType_Local
;
459 face
->mURI
= nullptr;
460 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::None
;
464 case StyleFontFaceSourceListComponent::Tag::Url
: {
465 face
->mSourceType
= gfxFontFaceSrc::eSourceType_URL
;
466 const StyleCssUrl
* url
= component
.AsUrl();
467 nsIURI
* uri
= url
->GetURI();
468 face
->mURI
= uri
? new gfxFontSrcURI(uri
) : nullptr;
469 const URLExtraData
& extraData
= url
->ExtraData();
470 face
->mReferrerInfo
= extraData
.ReferrerInfo();
472 // agent and user stylesheets are treated slightly differently,
473 // the same-site origin check and access control headers are
474 // enforced against the sheet principal rather than the document
475 // principal to allow user stylesheets to include @font-face rules
476 if (aOrigin
== StyleOrigin::User
||
477 aOrigin
== StyleOrigin::UserAgent
) {
478 face
->mUseOriginPrincipal
= true;
479 face
->mOriginPrincipal
= new gfxFontSrcPrincipal(
480 extraData
.Principal(), extraData
.Principal());
483 face
->mLocalName
.Truncate();
484 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::None
;
485 face
->mTechFlags
= StyleFontFaceSourceTechFlags::Empty();
488 // Check for a format hint.
489 const auto& next
= aAttr
.mSources
[i
+ 1];
491 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword
:
492 face
->mFormatHint
= next
.format_hint_keyword
._0
;
495 case StyleFontFaceSourceListComponent::Tag::FormatHintString
: {
496 nsDependentCSubstring
valueString(
497 reinterpret_cast<const char*>(
498 next
.format_hint_string
.utf8_bytes
),
499 next
.format_hint_string
.length
);
501 if (valueString
.LowerCaseEqualsASCII("woff")) {
502 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::Woff
;
503 } else if (valueString
.LowerCaseEqualsASCII("woff2")) {
504 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::Woff2
;
505 } else if (valueString
.LowerCaseEqualsASCII("opentype")) {
507 StyleFontFaceSourceFormatKeyword::Opentype
;
508 } else if (valueString
.LowerCaseEqualsASCII("truetype")) {
510 StyleFontFaceSourceFormatKeyword::Truetype
;
511 } else if (valueString
.LowerCaseEqualsASCII("truetype-aat")) {
513 StyleFontFaceSourceFormatKeyword::Truetype
;
514 } else if (valueString
.LowerCaseEqualsASCII(
515 "embedded-opentype")) {
517 StyleFontFaceSourceFormatKeyword::EmbeddedOpentype
;
518 } else if (valueString
.LowerCaseEqualsASCII("svg")) {
519 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::Svg
;
520 } else if (StaticPrefs::layout_css_font_variations_enabled()) {
521 // Non-standard values that Firefox accepted, for back-compat;
522 // these are superseded by the tech() function.
523 if (valueString
.LowerCaseEqualsASCII("woff-variations")) {
524 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::Woff
;
525 } else if (valueString
.LowerCaseEqualsASCII(
526 "woff2-variations")) {
527 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::Woff2
;
528 } else if (valueString
.LowerCaseEqualsASCII(
529 "opentype-variations")) {
531 StyleFontFaceSourceFormatKeyword::Opentype
;
532 } else if (valueString
.LowerCaseEqualsASCII(
533 "truetype-variations")) {
535 StyleFontFaceSourceFormatKeyword::Truetype
;
538 StyleFontFaceSourceFormatKeyword::Unknown
;
541 // unknown format specified, mark to distinguish from the
542 // case where no format hints are specified
543 face
->mFormatHint
= StyleFontFaceSourceFormatKeyword::Unknown
;
548 case StyleFontFaceSourceListComponent::Tag::TechFlags
:
549 case StyleFontFaceSourceListComponent::Tag::Local
:
550 case StyleFontFaceSourceListComponent::Tag::Url
:
556 // Check for a set of font-technologies flags.
557 const auto& next
= aAttr
.mSources
[i
+ 1];
558 if (next
.IsTechFlags()) {
559 face
->mTechFlags
= next
.AsTechFlags();
565 // if URI not valid, omit from src array
566 srcArray
.RemoveLastElement();
567 NS_WARNING("null url in @font-face rule");
573 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword
:
574 case StyleFontFaceSourceListComponent::Tag::FormatHintString
:
575 case StyleFontFaceSourceListComponent::Tag::TechFlags
:
576 MOZ_ASSERT_UNREACHABLE(
577 "Should always come after a URL source, and be consumed already");
583 if (srcArray
.IsEmpty()) {
587 return set
->FindOrCreateUserFontEntry(std::move(srcArray
), std::move(aAttr
));
590 nsresult
FontFaceSetImpl::LogMessage(gfxUserFontEntry
* aUserFontEntry
,
591 uint32_t aSrcIndex
, const char* aMessage
,
592 uint32_t aFlags
, nsresult aStatus
) {
593 nsAutoCString familyName
;
594 nsAutoCString fontURI
;
595 aUserFontEntry
->GetFamilyNameAndURIForLogging(aSrcIndex
, familyName
, fontURI
);
597 nsAutoCString weightString
;
598 aUserFontEntry
->Weight().ToString(weightString
);
599 nsAutoCString stretchString
;
600 aUserFontEntry
->Stretch().ToString(stretchString
);
601 nsPrintfCString
message(
602 "downloadable font: %s "
603 "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
604 aMessage
, familyName
.get(),
605 aUserFontEntry
->IsItalic() ? "italic" : "normal", // XXX todo: oblique?
606 weightString
.get(), stretchString
.get(), aSrcIndex
);
608 if (NS_FAILED(aStatus
)) {
609 message
.AppendLiteral(": ");
611 case NS_ERROR_DOM_BAD_URI
:
612 message
.AppendLiteral("bad URI or cross-site access not allowed");
614 case NS_ERROR_CONTENT_BLOCKED
:
615 message
.AppendLiteral("content blocked");
618 message
.AppendLiteral("status=");
619 message
.AppendInt(static_cast<uint32_t>(aStatus
));
623 message
.AppendLiteral(" source: ");
624 message
.Append(fontURI
);
626 LOG(("userfonts (%p) %s", this, message
.get()));
628 if (GetCurrentThreadWorkerPrivate()) {
629 // TODO(aosmond): Log to the console for workers. See bug 1778537.
633 nsCOMPtr
<nsIConsoleService
> console(
634 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
636 return NS_ERROR_NOT_AVAILABLE
;
639 // try to give the user an indication of where the rule came from
640 StyleLockedFontFaceRule
* rule
= FindRuleForUserFontEntry(aUserFontEntry
);
646 Servo_FontFaceRule_GetCssText(rule
, &text
);
647 Servo_FontFaceRule_GetSourceLocation(rule
, &line
, &column
);
648 // FIXME We need to figure out an approach to get the style sheet
649 // of this raw rule. See bug 1450903.
651 StyleSheet
* sheet
= rule
->GetStyleSheet();
652 // if the style sheet is removed while the font is loading can be null
654 nsCString spec
= sheet
->GetSheetURI()->GetSpecOrDefault();
655 CopyUTF8toUTF16(spec
, href
);
657 NS_WARNING("null parent stylesheet for @font-face rule");
658 href
.AssignLiteral("unknown");
661 // Leave href empty if we don't know how to get the correct sheet.
665 nsCOMPtr
<nsIScriptError
> scriptError
=
666 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
);
667 NS_ENSURE_SUCCESS(rv
, rv
);
669 rv
= scriptError
->InitWithWindowID(NS_ConvertUTF8toUTF16(message
),
671 NS_ConvertUTF8toUTF16(text
), // src line
674 "CSS Loader", // category (make separate?)
676 if (NS_SUCCEEDED(rv
)) {
677 console
->LogMessage(scriptError
);
683 nsresult
FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry
* aFontToLoad
,
684 const gfxFontFaceSrc
* aFontFaceSrc
,
686 uint32_t& aBufferLength
) {
687 nsCOMPtr
<nsIChannel
> channel
;
688 nsresult rv
= CreateChannelForSyncLoadFontData(getter_AddRefs(channel
),
689 aFontToLoad
, aFontFaceSrc
);
690 NS_ENSURE_SUCCESS(rv
, rv
);
692 // blocking stream is OK for data URIs
693 nsCOMPtr
<nsIInputStream
> stream
;
694 rv
= channel
->Open(getter_AddRefs(stream
));
695 NS_ENSURE_SUCCESS(rv
, rv
);
697 uint64_t bufferLength64
;
698 rv
= stream
->Available(&bufferLength64
);
699 NS_ENSURE_SUCCESS(rv
, rv
);
700 if (bufferLength64
== 0) {
701 return NS_ERROR_FAILURE
;
703 if (bufferLength64
> UINT32_MAX
) {
704 return NS_ERROR_FILE_TOO_BIG
;
706 aBufferLength
= static_cast<uint32_t>(bufferLength64
);
708 // read all the decoded data
709 aBuffer
= static_cast<uint8_t*>(malloc(sizeof(uint8_t) * aBufferLength
));
712 return NS_ERROR_OUT_OF_MEMORY
;
715 uint32_t numRead
, totalRead
= 0;
717 rv
= stream
->Read(reinterpret_cast<char*>(aBuffer
+ totalRead
),
718 aBufferLength
- totalRead
, &numRead
)) &&
720 totalRead
+= numRead
;
721 if (totalRead
> aBufferLength
) {
722 rv
= NS_ERROR_FAILURE
;
727 // make sure there's a mime type
728 if (NS_SUCCEEDED(rv
)) {
729 nsAutoCString mimeType
;
730 rv
= channel
->GetContentType(mimeType
);
731 aBufferLength
= totalRead
;
744 void FontFaceSetImpl::OnFontFaceStatusChanged(FontFaceImpl
* aFontFace
) {
745 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
746 RecursiveMutexAutoLock
lock(mMutex
);
747 MOZ_ASSERT(HasAvailableFontFace(aFontFace
));
749 mHasLoadingFontFacesIsDirty
= true;
751 if (aFontFace
->Status() == FontFaceLoadStatus::Loading
) {
752 CheckLoadingStarted();
754 MOZ_ASSERT(aFontFace
->Status() == FontFaceLoadStatus::Loaded
||
755 aFontFace
->Status() == FontFaceLoadStatus::Error
);
756 // When a font finishes downloading, nsPresContext::UserFontSetUpdated
757 // will be called immediately afterwards to request a reflow of the
758 // relevant elements in the document. We want to wait until the reflow
759 // request has been done before the FontFaceSet is marked as Loaded so
760 // that we don't briefly set the FontFaceSet to Loaded and then Loading
761 // again once the reflow is pending. So we go around the event loop
762 // and call CheckLoadingFinished() after the reflow has been queued.
763 if (!mDelayedLoadCheck
) {
764 mDelayedLoadCheck
= true;
765 DispatchCheckLoadingFinishedAfterDelay();
770 void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() {
771 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
773 if (ServoStyleSet
* set
= gfxFontUtils::CurrentServoStyleSet()) {
774 // See comments in Gecko_GetFontMetrics.
776 // We can't just dispatch the runnable below if we're not on the main
777 // thread, since it needs to take a strong reference to the FontFaceSet,
778 // and being a DOM object, FontFaceSet doesn't support thread-safe
781 PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(
786 DispatchToOwningThread(
787 "FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay",
788 [self
= RefPtr
{this}]() { self
->CheckLoadingFinishedAfterDelay(); });
791 void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() {
792 RecursiveMutexAutoLock
lock(mMutex
);
793 mDelayedLoadCheck
= false;
794 CheckLoadingFinished();
797 void FontFaceSetImpl::CheckLoadingStarted() {
798 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
799 RecursiveMutexAutoLock
lock(mMutex
);
801 if (!HasLoadingFontFaces()) {
805 if (mStatus
== FontFaceSetLoadStatus::Loading
) {
806 // We have already dispatched a loading event and replaced mReady
807 // with a fresh, unresolved promise.
811 mStatus
= FontFaceSetLoadStatus::Loading
;
813 if (IsOnOwningThread()) {
818 DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted",
819 [self
= RefPtr
{this}]() { self
->OnLoadingStarted(); });
822 void FontFaceSetImpl::OnLoadingStarted() {
823 RecursiveMutexAutoLock
lock(mMutex
);
825 mOwner
->DispatchLoadingEventAndReplaceReadyPromise();
829 void FontFaceSetImpl::UpdateHasLoadingFontFaces() {
830 RecursiveMutexAutoLock
lock(mMutex
);
831 mHasLoadingFontFacesIsDirty
= false;
832 mHasLoadingFontFaces
= false;
833 for (size_t i
= 0; i
< mNonRuleFaces
.Length(); i
++) {
834 if (mNonRuleFaces
[i
].mFontFace
->Status() == FontFaceLoadStatus::Loading
) {
835 mHasLoadingFontFaces
= true;
841 bool FontFaceSetImpl::HasLoadingFontFaces() {
842 RecursiveMutexAutoLock
lock(mMutex
);
843 if (mHasLoadingFontFacesIsDirty
) {
844 UpdateHasLoadingFontFaces();
846 return mHasLoadingFontFaces
;
849 bool FontFaceSetImpl::MightHavePendingFontLoads() {
850 // Check for FontFace objects in the FontFaceSet that are still loading.
851 return HasLoadingFontFaces();
854 void FontFaceSetImpl::CheckLoadingFinished() {
855 RecursiveMutexAutoLock
lock(mMutex
);
856 if (mDelayedLoadCheck
) {
857 // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
861 if (!ReadyPromiseIsPending()) {
862 // We've already resolved mReady (or set the flag to do that lazily) and
863 // dispatched the loadingdone/loadingerror events.
867 if (MightHavePendingFontLoads()) {
868 // We're not finished loading yet.
872 mStatus
= FontFaceSetLoadStatus::Loaded
;
874 if (IsOnOwningThread()) {
879 DispatchToOwningThread(
880 "FontFaceSetImpl::CheckLoadingFinished",
881 [self
= RefPtr
{this}]() { self
->OnLoadingFinished(); });
884 void FontFaceSetImpl::OnLoadingFinished() {
885 RecursiveMutexAutoLock
lock(mMutex
);
887 mOwner
->MaybeResolve();
891 void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
892 RecursiveMutexAutoLock
lock(mMutex
);
893 mAllowedFontLoads
.Clear();
894 IncrementGeneration(false);
898 // ------------------------------------------------
900 already_AddRefed
<gfxFontSrcPrincipal
>
901 FontFaceSetImpl::GetStandardFontLoadPrincipal() const {
902 RecursiveMutexAutoLock
lock(mMutex
);
903 return RefPtr
{mStandardFontLoadPrincipal
}.forget();
906 void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize
,
907 TimeStamp aDoneTime
) {
909 mDownloadSize
+= aFontSize
;
910 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE
, aFontSize
/ 1024);
912 TimeStamp navStart
= GetNavigationStartTimeStamp();
914 if (navStart
!= zero
) {
915 mozilla::glean::network::font_download_end
.AccumulateRawDuration(aDoneTime
-
920 void FontFaceSetImpl::DoRebuildUserFontSet() { MarkUserFontSetDirty(); }
922 already_AddRefed
<gfxUserFontEntry
> FontFaceSetImpl::CreateUserFontEntry(
923 nsTArray
<gfxFontFaceSrc
>&& aFontFaceSrcList
,
924 gfxUserFontAttributes
&& aAttr
) {
925 RefPtr
<gfxUserFontEntry
> entry
= new FontFaceImpl::Entry(
926 this, std::move(aFontFaceSrcList
), std::move(aAttr
));
927 return entry
.forget();
930 void FontFaceSetImpl::ForgetLocalFaces() {
931 // We cannot hold our lock at the same time as the gfxUserFontFamily lock, so
932 // we need to make a copy of the table first.
933 nsTArray
<RefPtr
<gfxUserFontFamily
>> fontFamilies
;
935 RecursiveMutexAutoLock
lock(mMutex
);
936 fontFamilies
.SetCapacity(mFontFamilies
.Count());
937 for (const auto& fam
: mFontFamilies
.Values()) {
938 fontFamilies
.AppendElement(fam
);
942 for (const auto& fam
: fontFamilies
) {
943 ForgetLocalFace(fam
);
947 already_AddRefed
<gfxUserFontFamily
> FontFaceSetImpl::GetFamily(
948 const nsACString
& aFamilyName
) {
949 RecursiveMutexAutoLock
lock(mMutex
);
950 return gfxUserFontSet::GetFamily(aFamilyName
);