Bug 1879449 [wpt PR 44489] - [wptrunner] Add `infrastructure/expected-fail/` test...
[gecko.git] / layout / style / FontFaceSetImpl.cpp
blob7383842a417b0e7c933fe6a7cc1142087f894ae1
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;
65 #define LOG(args) \
66 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, args)
67 #define LOG_ENABLED() \
68 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
70 NS_IMPL_ISUPPORTS0(FontFaceSetImpl)
72 FontFaceSetImpl::FontFaceSetImpl(FontFaceSet* aOwner)
73 : mMutex("mozilla::dom::FontFaceSetImpl"),
74 mOwner(aOwner),
75 mStatus(FontFaceSetLoadStatus::Loaded),
76 mNonRuleFacesDirty(false),
77 mHasLoadingFontFaces(false),
78 mHasLoadingFontFacesIsDirty(false),
79 mDelayedLoadCheck(false),
80 mBypassCache(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());
88 Destroy();
91 void FontFaceSetImpl::DestroyLoaders() {
92 mMutex.AssertCurrentThreadIn();
93 if (mLoaders.IsEmpty()) {
94 return;
96 if (NS_IsMainThread()) {
97 for (const auto& key : mLoaders.Keys()) {
98 key->Cancel();
100 mLoaders.Clear();
101 return;
104 class DestroyLoadersRunnable final : public Runnable {
105 public:
106 explicit DestroyLoadersRunnable(FontFaceSetImpl* aFontFaceSet)
107 : Runnable("FontFaceSetImpl::DestroyLoaders"),
108 mFontFaceSet(aFontFaceSet) {}
110 protected:
111 ~DestroyLoadersRunnable() override = default;
113 NS_IMETHOD Run() override {
114 RecursiveMutexAutoLock lock(mFontFaceSet->mMutex);
115 mFontFaceSet->DestroyLoaders();
116 return NS_OK;
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);
134 DestroyLoaders();
135 nonRuleFaces = std::move(mNonRuleFaces);
136 fontFamilies = std::move(mFontFamilies);
137 mOwner = nullptr;
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,
148 ErrorResult& aRv) {
149 RefPtr<URLExtraData> url = GetURLExtraData();
150 if (!url) {
151 aRv.ThrowInvalidStateError("Missing URLExtraData");
152 return;
155 if (!ServoCSSParser::ParseFontShorthandForMatching(
156 aFont, url, aFamilyList, aStyle, aStretch, aWeight)) {
157 aRv.ThrowSyntaxError("Invalid font shorthand");
158 return;
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();
167 while (p < end) {
168 uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
169 if (aEntry->CharacterInUnicodeRange(c)) {
170 return true;
173 return false;
176 void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont,
177 const nsAString& aText,
178 nsTArray<FontFace*>& aFontFaces,
179 ErrorResult& aRv) {
180 RecursiveMutexAutoLock lock(mMutex);
182 StyleFontFamilyList familyList;
183 FontWeight weight;
184 FontStretch stretch;
185 FontSlantStyle italicStyle;
186 ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
187 aRv);
188 if (aRv.Failed()) {
189 return;
192 gfxFontStyle style;
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()) {
202 continue;
205 const auto& name = fontFamilyName.AsFamilyName();
206 RefPtr<gfxFontFamily> family =
207 LookupFamily(nsAtomCString(name.name.AsAtom()));
209 if (!family) {
210 continue;
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()) {
225 return;
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);
252 FlushUserFontSet();
253 return mStatus;
256 bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
257 RecursiveMutexAutoLock lock(mMutex);
258 FlushUserFontSet();
260 if (aFontFace->IsInFontFaceSet(this)) {
261 return false;
264 if (aFontFace->HasRule()) {
265 aRv.ThrowInvalidModificationError(
266 "Can't add face to FontFaceSet that comes from an @font-face rule");
267 return false;
270 aFontFace->AddFontFaceSet(this);
272 #ifdef DEBUG
273 for (const FontFaceRecord& rec : mNonRuleFaces) {
274 MOZ_ASSERT(rec.mFontFace != aFontFace,
275 "FontFace should not occur in mNonRuleFaces twice");
277 #endif
279 FontFaceRecord* rec = mNonRuleFaces.AppendElement();
280 rec->mFontFace = aFontFace;
281 rec->mOrigin = Nothing();
283 mNonRuleFacesDirty = true;
284 MarkUserFontSetDirty();
285 mHasLoadingFontFacesIsDirty = true;
286 CheckLoadingStarted();
287 return true;
290 void FontFaceSetImpl::Clear() {
291 RecursiveMutexAutoLock lock(mMutex);
292 FlushUserFontSet();
294 if (mNonRuleFaces.IsEmpty()) {
295 return;
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);
312 FlushUserFontSet();
314 if (aFontFace->HasRule()) {
315 return false;
318 bool removed = false;
319 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
320 if (mNonRuleFaces[i].mFontFace == aFontFace) {
321 mNonRuleFaces.RemoveElementAt(i);
322 removed = true;
323 break;
326 if (!removed) {
327 return false;
330 aFontFace->RemoveFontFaceSet(this);
332 mNonRuleFacesDirty = true;
333 MarkUserFontSetDirty();
334 mHasLoadingFontFacesIsDirty = true;
335 CheckLoadingFinished();
336 return true;
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.
353 return;
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);
363 if (!entry) {
364 return;
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
382 // family.
383 if (resetFamilyName) {
384 RefPtr<gfxUserFontFamily> family = LookupFamily(aEntry->mFamilyName);
385 if (family) {
386 family->RemoveFontEntry(aEntry);
388 aEntry->mFamilyName.Truncate(0);
392 class FontFaceSetImpl::UpdateUserFontEntryRunnable final
393 : public WorkerMainThreadRunnable {
394 public:
395 UpdateUserFontEntryRunnable(FontFaceSetImpl* aSet, gfxUserFontEntry* aEntry,
396 gfxUserFontAttributes& aAttr)
397 : WorkerMainThreadRunnable(
398 GetCurrentThreadWorkerPrivate(),
399 "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns),
400 mSet(aSet),
401 mEntry(aEntry),
402 mAttr(aAttr) {}
404 bool MainThreadRun() override {
405 mSet->UpdateUserFontEntry(mEntry, std::move(mAttr));
406 return true;
409 private:
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.
418 /* static */
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();
426 if (existingEntry) {
427 if (NS_IsMainThread()) {
428 set->UpdateUserFontEntry(existingEntry, std::move(aAttr));
429 } else {
430 auto task =
431 MakeRefPtr<UpdateUserFontEntryRunnable>(set, existingEntry, aAttr);
432 IgnoredErrorResult ignoredRv;
433 task->Dispatch(Canceling, ignoredRv);
435 return existingEntry.forget();
438 // set up src array
439 nsTArray<gfxFontFaceSrc> srcArray;
441 if (aFontFace->HasFontData()) {
442 gfxFontFaceSrc* face = srcArray.AppendElement();
443 if (!face) {
444 return nullptr;
447 face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
448 face->mBuffer = aFontFace->TakeBufferSource();
449 } else {
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;
461 break;
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();
487 if (i + 1 < len) {
488 // Check for a format hint.
489 const auto& next = aAttr.mSources[i + 1];
490 switch (next.tag) {
491 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
492 face->mFormatHint = next.format_hint_keyword._0;
493 i++;
494 break;
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")) {
506 face->mFormatHint =
507 StyleFontFaceSourceFormatKeyword::Opentype;
508 } else if (valueString.LowerCaseEqualsASCII("truetype")) {
509 face->mFormatHint =
510 StyleFontFaceSourceFormatKeyword::Truetype;
511 } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
512 face->mFormatHint =
513 StyleFontFaceSourceFormatKeyword::Truetype;
514 } else if (valueString.LowerCaseEqualsASCII(
515 "embedded-opentype")) {
516 face->mFormatHint =
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")) {
530 face->mFormatHint =
531 StyleFontFaceSourceFormatKeyword::Opentype;
532 } else if (valueString.LowerCaseEqualsASCII(
533 "truetype-variations")) {
534 face->mFormatHint =
535 StyleFontFaceSourceFormatKeyword::Truetype;
536 } else {
537 face->mFormatHint =
538 StyleFontFaceSourceFormatKeyword::Unknown;
540 } else {
541 // unknown format specified, mark to distinguish from the
542 // case where no format hints are specified
543 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Unknown;
545 i++;
546 break;
548 case StyleFontFaceSourceListComponent::Tag::TechFlags:
549 case StyleFontFaceSourceListComponent::Tag::Local:
550 case StyleFontFaceSourceListComponent::Tag::Url:
551 break;
555 if (i + 1 < len) {
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();
560 i++;
564 if (!face->mURI) {
565 // if URI not valid, omit from src array
566 srcArray.RemoveLastElement();
567 NS_WARNING("null url in @font-face rule");
568 continue;
570 break;
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");
578 break;
583 if (srcArray.IsEmpty()) {
584 return nullptr;
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(": ");
610 switch (aStatus) {
611 case NS_ERROR_DOM_BAD_URI:
612 message.AppendLiteral("bad URI or cross-site access not allowed");
613 break;
614 case NS_ERROR_CONTENT_BLOCKED:
615 message.AppendLiteral("content blocked");
616 break;
617 default:
618 message.AppendLiteral("status=");
619 message.AppendInt(static_cast<uint32_t>(aStatus));
620 break;
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.
630 return NS_OK;
633 nsCOMPtr<nsIConsoleService> console(
634 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
635 if (!console) {
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);
641 nsString href;
642 nsAutoCString text;
643 uint32_t line = 0;
644 uint32_t column = 0;
645 if (rule) {
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.
650 #if 0
651 StyleSheet* sheet = rule->GetStyleSheet();
652 // if the style sheet is removed while the font is loading can be null
653 if (sheet) {
654 nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
655 CopyUTF8toUTF16(spec, href);
656 } else {
657 NS_WARNING("null parent stylesheet for @font-face rule");
658 href.AssignLiteral("unknown");
660 #endif
661 // Leave href empty if we don't know how to get the correct sheet.
664 nsresult rv;
665 nsCOMPtr<nsIScriptError> scriptError =
666 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
667 NS_ENSURE_SUCCESS(rv, rv);
669 rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
670 href, // file
671 NS_ConvertUTF8toUTF16(text), // src line
672 line, column,
673 aFlags, // flags
674 "CSS Loader", // category (make separate?)
675 GetInnerWindowID());
676 if (NS_SUCCEEDED(rv)) {
677 console->LogMessage(scriptError);
680 return NS_OK;
683 nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
684 const gfxFontFaceSrc* aFontFaceSrc,
685 uint8_t*& aBuffer,
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));
710 if (!aBuffer) {
711 aBufferLength = 0;
712 return NS_ERROR_OUT_OF_MEMORY;
715 uint32_t numRead, totalRead = 0;
716 while (NS_SUCCEEDED(
717 rv = stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
718 aBufferLength - totalRead, &numRead)) &&
719 numRead != 0) {
720 totalRead += numRead;
721 if (totalRead > aBufferLength) {
722 rv = NS_ERROR_FAILURE;
723 break;
727 // make sure there's a mime type
728 if (NS_SUCCEEDED(rv)) {
729 nsAutoCString mimeType;
730 rv = channel->GetContentType(mimeType);
731 aBufferLength = totalRead;
734 if (NS_FAILED(rv)) {
735 free(aBuffer);
736 aBuffer = nullptr;
737 aBufferLength = 0;
738 return rv;
741 return NS_OK;
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();
753 } else {
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
779 // refcounting.
780 set->AppendTask(
781 PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(
782 this));
783 return;
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()) {
802 return;
805 if (mStatus == FontFaceSetLoadStatus::Loading) {
806 // We have already dispatched a loading event and replaced mReady
807 // with a fresh, unresolved promise.
808 return;
811 mStatus = FontFaceSetLoadStatus::Loading;
813 if (IsOnOwningThread()) {
814 OnLoadingStarted();
815 return;
818 DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted",
819 [self = RefPtr{this}]() { self->OnLoadingStarted(); });
822 void FontFaceSetImpl::OnLoadingStarted() {
823 RecursiveMutexAutoLock lock(mMutex);
824 if (mOwner) {
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;
836 return;
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.
858 return;
861 if (!ReadyPromiseIsPending()) {
862 // We've already resolved mReady (or set the flag to do that lazily) and
863 // dispatched the loadingdone/loadingerror events.
864 return;
867 if (MightHavePendingFontLoads()) {
868 // We're not finished loading yet.
869 return;
872 mStatus = FontFaceSetLoadStatus::Loaded;
874 if (IsOnOwningThread()) {
875 OnLoadingFinished();
876 return;
879 DispatchToOwningThread(
880 "FontFaceSetImpl::CheckLoadingFinished",
881 [self = RefPtr{this}]() { self->OnLoadingFinished(); });
884 void FontFaceSetImpl::OnLoadingFinished() {
885 RecursiveMutexAutoLock lock(mMutex);
886 if (mOwner) {
887 mOwner->MaybeResolve();
891 void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
892 RecursiveMutexAutoLock lock(mMutex);
893 mAllowedFontLoads.Clear();
894 IncrementGeneration(false);
897 // -- gfxUserFontSet
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) {
908 mDownloadCount++;
909 mDownloadSize += aFontSize;
910 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024);
912 TimeStamp navStart = GetNavigationStartTimeStamp();
913 TimeStamp zero;
914 if (navStart != zero) {
915 mozilla::glean::network::font_download_end.AccumulateRawDuration(aDoneTime -
916 navStart);
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);
953 #undef LOG_ENABLED
954 #undef LOG