No bug - tagging b4d3227540c9ebc43d64aac6168fdca7019c22d8 with FIREFOX_BETA_126_BASE...
[gecko.git] / layout / style / FontFaceSetImpl.cpp
blob5dabf25d384af59e6d04f601ea25c974f761c0d4
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 : mOwner(aOwner),
74 mStatus(FontFaceSetLoadStatus::Loaded),
75 mNonRuleFacesDirty(false),
76 mHasLoadingFontFaces(false),
77 mHasLoadingFontFacesIsDirty(false),
78 mDelayedLoadCheck(false),
79 mBypassCache(false),
80 mPrivateBrowsing(false) {}
82 FontFaceSetImpl::~FontFaceSetImpl() {
83 // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
84 // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
85 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
87 Destroy();
90 void FontFaceSetImpl::DestroyLoaders() {
91 mMutex.AssertCurrentThreadIn();
92 if (mLoaders.IsEmpty()) {
93 return;
95 if (NS_IsMainThread()) {
96 for (const auto& key : mLoaders.Keys()) {
97 key->Cancel();
99 mLoaders.Clear();
100 return;
103 class DestroyLoadersRunnable final : public Runnable {
104 public:
105 explicit DestroyLoadersRunnable(FontFaceSetImpl* aFontFaceSet)
106 : Runnable("FontFaceSetImpl::DestroyLoaders"),
107 mFontFaceSet(aFontFaceSet) {}
109 protected:
110 ~DestroyLoadersRunnable() override = default;
112 NS_IMETHOD Run() override {
113 RecursiveMutexAutoLock lock(mFontFaceSet->mMutex);
114 mFontFaceSet->DestroyLoaders();
115 return NS_OK;
118 // We need to save a reference to the FontFaceSetImpl because the
119 // loaders contain a non-owning reference to it.
120 RefPtr<FontFaceSetImpl> mFontFaceSet;
123 auto runnable = MakeRefPtr<DestroyLoadersRunnable>(this);
124 NS_DispatchToMainThread(runnable);
127 void FontFaceSetImpl::Destroy() {
128 nsTArray<FontFaceRecord> nonRuleFaces;
129 nsRefPtrHashtable<nsCStringHashKey, gfxUserFontFamily> fontFamilies;
132 RecursiveMutexAutoLock lock(mMutex);
133 DestroyLoaders();
134 nonRuleFaces = std::move(mNonRuleFaces);
135 fontFamilies = std::move(mFontFamilies);
136 mOwner = nullptr;
139 if (gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList()) {
140 fp->RemoveUserFontSet(this);
144 void FontFaceSetImpl::ParseFontShorthandForMatching(
145 const nsACString& aFont, StyleFontFamilyList& aFamilyList,
146 FontWeight& aWeight, FontStretch& aStretch, FontSlantStyle& aStyle,
147 ErrorResult& aRv) {
148 RefPtr<URLExtraData> url = GetURLExtraData();
149 if (!url) {
150 aRv.ThrowInvalidStateError("Missing URLExtraData");
151 return;
154 if (!ServoCSSParser::ParseFontShorthandForMatching(
155 aFont, url, aFamilyList, aStyle, aStretch, aWeight)) {
156 aRv.ThrowSyntaxError("Invalid font shorthand");
157 return;
161 static bool HasAnyCharacterInUnicodeRange(gfxUserFontEntry* aEntry,
162 const nsAString& aInput) {
163 const char16_t* p = aInput.Data();
164 const char16_t* end = p + aInput.Length();
166 while (p < end) {
167 uint32_t c = UTF16CharEnumerator::NextChar(&p, end);
168 if (aEntry->CharacterInUnicodeRange(c)) {
169 return true;
172 return false;
175 void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont,
176 const nsAString& aText,
177 nsTArray<FontFace*>& aFontFaces,
178 ErrorResult& aRv) {
179 RecursiveMutexAutoLock lock(mMutex);
181 StyleFontFamilyList familyList;
182 FontWeight weight;
183 FontStretch stretch;
184 FontSlantStyle italicStyle;
185 ParseFontShorthandForMatching(aFont, familyList, weight, stretch, italicStyle,
186 aRv);
187 if (aRv.Failed()) {
188 return;
191 gfxFontStyle style;
192 style.style = italicStyle;
193 style.weight = weight;
194 style.stretch = stretch;
196 // Set of FontFaces that we want to return.
197 nsTHashSet<FontFace*> matchingFaces;
199 for (const StyleSingleFontFamily& fontFamilyName : familyList.list.AsSpan()) {
200 if (!fontFamilyName.IsFamilyName()) {
201 continue;
204 const auto& name = fontFamilyName.AsFamilyName();
205 RefPtr<gfxFontFamily> family =
206 LookupFamily(nsAtomCString(name.name.AsAtom()));
208 if (!family) {
209 continue;
212 AutoTArray<gfxFontEntry*, 4> entries;
213 family->FindAllFontsForStyle(style, entries);
215 for (gfxFontEntry* e : entries) {
216 FontFaceImpl::Entry* entry = static_cast<FontFaceImpl::Entry*>(e);
217 if (HasAnyCharacterInUnicodeRange(entry, aText)) {
218 entry->FindFontFaceOwners(matchingFaces);
223 if (matchingFaces.IsEmpty()) {
224 return;
227 // Add all FontFaces in matchingFaces to aFontFaces, in the order
228 // they appear in the FontFaceSet.
229 FindMatchingFontFaces(matchingFaces, aFontFaces);
232 void FontFaceSetImpl::FindMatchingFontFaces(
233 const nsTHashSet<FontFace*>& aMatchingFaces,
234 nsTArray<FontFace*>& aFontFaces) {
235 RecursiveMutexAutoLock lock(mMutex);
236 for (FontFaceRecord& record : mNonRuleFaces) {
237 FontFace* owner = record.mFontFace->GetOwner();
238 if (owner && aMatchingFaces.Contains(owner)) {
239 aFontFaces.AppendElement(owner);
244 bool FontFaceSetImpl::ReadyPromiseIsPending() const {
245 RecursiveMutexAutoLock lock(mMutex);
246 return mOwner && mOwner->ReadyPromiseIsPending();
249 FontFaceSetLoadStatus FontFaceSetImpl::Status() {
250 RecursiveMutexAutoLock lock(mMutex);
251 FlushUserFontSet();
252 return mStatus;
255 bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) {
256 RecursiveMutexAutoLock lock(mMutex);
257 FlushUserFontSet();
259 if (aFontFace->IsInFontFaceSet(this)) {
260 return false;
263 if (aFontFace->HasRule()) {
264 aRv.ThrowInvalidModificationError(
265 "Can't add face to FontFaceSet that comes from an @font-face rule");
266 return false;
269 aFontFace->AddFontFaceSet(this);
271 #ifdef DEBUG
272 for (const FontFaceRecord& rec : mNonRuleFaces) {
273 MOZ_ASSERT(rec.mFontFace != aFontFace,
274 "FontFace should not occur in mNonRuleFaces twice");
276 #endif
278 FontFaceRecord* rec = mNonRuleFaces.AppendElement();
279 rec->mFontFace = aFontFace;
280 rec->mOrigin = Nothing();
282 mNonRuleFacesDirty = true;
283 MarkUserFontSetDirty();
284 mHasLoadingFontFacesIsDirty = true;
285 CheckLoadingStarted();
286 return true;
289 void FontFaceSetImpl::Clear() {
290 RecursiveMutexAutoLock lock(mMutex);
291 FlushUserFontSet();
293 if (mNonRuleFaces.IsEmpty()) {
294 return;
297 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
298 FontFaceImpl* f = mNonRuleFaces[i].mFontFace;
299 f->RemoveFontFaceSet(this);
302 mNonRuleFaces.Clear();
303 mNonRuleFacesDirty = true;
304 MarkUserFontSetDirty();
305 mHasLoadingFontFacesIsDirty = true;
306 CheckLoadingFinished();
309 bool FontFaceSetImpl::Delete(FontFaceImpl* aFontFace) {
310 RecursiveMutexAutoLock lock(mMutex);
311 FlushUserFontSet();
313 if (aFontFace->HasRule()) {
314 return false;
317 bool removed = false;
318 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
319 if (mNonRuleFaces[i].mFontFace == aFontFace) {
320 mNonRuleFaces.RemoveElementAt(i);
321 removed = true;
322 break;
325 if (!removed) {
326 return false;
329 aFontFace->RemoveFontFaceSet(this);
331 mNonRuleFacesDirty = true;
332 MarkUserFontSetDirty();
333 mHasLoadingFontFacesIsDirty = true;
334 CheckLoadingFinished();
335 return true;
338 bool FontFaceSetImpl::HasAvailableFontFace(FontFaceImpl* aFontFace) {
339 return aFontFace->IsInFontFaceSet(this);
342 void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader* aLoader) {
343 RecursiveMutexAutoLock lock(mMutex);
344 mLoaders.RemoveEntry(aLoader);
347 void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace) {
348 gfxUserFontAttributes attr;
349 if (!aFontFace->GetAttributes(attr)) {
350 // If there is no family name, this rule cannot contribute a
351 // usable font, so there is no point in processing it further.
352 return;
355 nsAutoCString family(attr.mFamilyName);
357 // Just create a new font entry if we haven't got one already.
358 if (!aFontFace->GetUserFontEntry()) {
359 // XXX Should we be checking mLocalRulesUsed like InsertRuleFontFace does?
360 RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace(
361 aFontFace, std::move(attr), StyleOrigin::Author);
362 if (!entry) {
363 return;
365 aFontFace->SetUserFontEntry(entry);
367 AddUserFontEntry(family, aFontFace->GetUserFontEntry());
370 void FontFaceSetImpl::UpdateUserFontEntry(gfxUserFontEntry* aEntry,
371 gfxUserFontAttributes&& aAttr) {
372 MOZ_ASSERT(NS_IsMainThread());
374 bool resetFamilyName = !aEntry->mFamilyName.IsEmpty() &&
375 aEntry->mFamilyName != aAttr.mFamilyName;
376 // aFontFace already has a user font entry, so we update its attributes
377 // rather than creating a new one.
378 aEntry->UpdateAttributes(std::move(aAttr));
379 // If the family name has changed, remove the entry from its current family
380 // and clear the mFamilyName field so it can be reset when added to a new
381 // family.
382 if (resetFamilyName) {
383 RefPtr<gfxUserFontFamily> family = LookupFamily(aEntry->mFamilyName);
384 if (family) {
385 family->RemoveFontEntry(aEntry);
387 aEntry->mFamilyName.Truncate(0);
391 class FontFaceSetImpl::UpdateUserFontEntryRunnable final
392 : public WorkerMainThreadRunnable {
393 public:
394 UpdateUserFontEntryRunnable(FontFaceSetImpl* aSet, gfxUserFontEntry* aEntry,
395 gfxUserFontAttributes& aAttr)
396 : WorkerMainThreadRunnable(
397 GetCurrentThreadWorkerPrivate(),
398 "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns),
399 mSet(aSet),
400 mEntry(aEntry),
401 mAttr(aAttr) {}
403 bool MainThreadRun() override {
404 mSet->UpdateUserFontEntry(mEntry, std::move(mAttr));
405 return true;
408 private:
409 FontFaceSetImpl* mSet;
410 gfxUserFontEntry* mEntry;
411 gfxUserFontAttributes& mAttr;
414 // TODO(emilio): Should this take an nsAtom* aFamilyName instead?
416 // All callers have one handy.
417 /* static */
418 already_AddRefed<gfxUserFontEntry>
419 FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace(
420 FontFaceImpl* aFontFace, gfxUserFontAttributes&& aAttr,
421 StyleOrigin aOrigin) {
422 FontFaceSetImpl* set = aFontFace->GetPrimaryFontFaceSet();
424 RefPtr<gfxUserFontEntry> existingEntry = aFontFace->GetUserFontEntry();
425 if (existingEntry) {
426 if (NS_IsMainThread()) {
427 set->UpdateUserFontEntry(existingEntry, std::move(aAttr));
428 } else {
429 auto task =
430 MakeRefPtr<UpdateUserFontEntryRunnable>(set, existingEntry, aAttr);
431 IgnoredErrorResult ignoredRv;
432 task->Dispatch(Canceling, ignoredRv);
434 return existingEntry.forget();
437 // set up src array
438 nsTArray<gfxFontFaceSrc> srcArray;
440 if (aFontFace->HasFontData()) {
441 gfxFontFaceSrc* face = srcArray.AppendElement();
442 if (!face) {
443 return nullptr;
446 face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer;
447 face->mBuffer = aFontFace->TakeBufferSource();
448 } else {
449 size_t len = aAttr.mSources.Length();
450 for (size_t i = 0; i < len; ++i) {
451 gfxFontFaceSrc* face = srcArray.AppendElement();
452 const auto& component = aAttr.mSources[i];
453 switch (component.tag) {
454 case StyleFontFaceSourceListComponent::Tag::Local: {
455 nsAtom* atom = component.AsLocal();
456 face->mLocalName.Append(nsAtomCString(atom));
457 face->mSourceType = gfxFontFaceSrc::eSourceType_Local;
458 face->mURI = nullptr;
459 face->mFormatHint = StyleFontFaceSourceFormatKeyword::None;
460 break;
463 case StyleFontFaceSourceListComponent::Tag::Url: {
464 face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
465 const StyleCssUrl* url = component.AsUrl();
466 nsIURI* uri = url->GetURI();
467 face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr;
468 const URLExtraData& extraData = url->ExtraData();
469 face->mReferrerInfo = extraData.ReferrerInfo();
471 // agent and user stylesheets are treated slightly differently,
472 // the same-site origin check and access control headers are
473 // enforced against the sheet principal rather than the document
474 // principal to allow user stylesheets to include @font-face rules
475 if (aOrigin == StyleOrigin::User ||
476 aOrigin == StyleOrigin::UserAgent) {
477 face->mUseOriginPrincipal = true;
478 face->mOriginPrincipal = new gfxFontSrcPrincipal(
479 extraData.Principal(), extraData.Principal());
482 face->mLocalName.Truncate();
483 face->mFormatHint = StyleFontFaceSourceFormatKeyword::None;
484 face->mTechFlags = StyleFontFaceSourceTechFlags::Empty();
486 if (i + 1 < len) {
487 // Check for a format hint.
488 const auto& next = aAttr.mSources[i + 1];
489 switch (next.tag) {
490 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
491 face->mFormatHint = next.format_hint_keyword._0;
492 i++;
493 break;
494 case StyleFontFaceSourceListComponent::Tag::FormatHintString: {
495 nsDependentCSubstring valueString(
496 reinterpret_cast<const char*>(
497 next.format_hint_string.utf8_bytes),
498 next.format_hint_string.length);
500 if (valueString.LowerCaseEqualsASCII("woff")) {
501 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff;
502 } else if (valueString.LowerCaseEqualsASCII("woff2")) {
503 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2;
504 } else if (valueString.LowerCaseEqualsASCII("opentype")) {
505 face->mFormatHint =
506 StyleFontFaceSourceFormatKeyword::Opentype;
507 } else if (valueString.LowerCaseEqualsASCII("truetype")) {
508 face->mFormatHint =
509 StyleFontFaceSourceFormatKeyword::Truetype;
510 } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
511 face->mFormatHint =
512 StyleFontFaceSourceFormatKeyword::Truetype;
513 } else if (valueString.LowerCaseEqualsASCII(
514 "embedded-opentype")) {
515 face->mFormatHint =
516 StyleFontFaceSourceFormatKeyword::EmbeddedOpentype;
517 } else if (valueString.LowerCaseEqualsASCII("svg")) {
518 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Svg;
519 } else if (StaticPrefs::layout_css_font_variations_enabled()) {
520 // Non-standard values that Firefox accepted, for back-compat;
521 // these are superseded by the tech() function.
522 if (valueString.LowerCaseEqualsASCII("woff-variations")) {
523 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff;
524 } else if (valueString.LowerCaseEqualsASCII(
525 "woff2-variations")) {
526 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2;
527 } else if (valueString.LowerCaseEqualsASCII(
528 "opentype-variations")) {
529 face->mFormatHint =
530 StyleFontFaceSourceFormatKeyword::Opentype;
531 } else if (valueString.LowerCaseEqualsASCII(
532 "truetype-variations")) {
533 face->mFormatHint =
534 StyleFontFaceSourceFormatKeyword::Truetype;
535 } else {
536 face->mFormatHint =
537 StyleFontFaceSourceFormatKeyword::Unknown;
539 } else {
540 // unknown format specified, mark to distinguish from the
541 // case where no format hints are specified
542 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Unknown;
544 i++;
545 break;
547 case StyleFontFaceSourceListComponent::Tag::TechFlags:
548 case StyleFontFaceSourceListComponent::Tag::Local:
549 case StyleFontFaceSourceListComponent::Tag::Url:
550 break;
554 if (i + 1 < len) {
555 // Check for a set of font-technologies flags.
556 const auto& next = aAttr.mSources[i + 1];
557 if (next.IsTechFlags()) {
558 face->mTechFlags = next.AsTechFlags();
559 i++;
563 if (!face->mURI) {
564 // if URI not valid, omit from src array
565 srcArray.RemoveLastElement();
566 NS_WARNING("null url in @font-face rule");
567 continue;
569 break;
572 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword:
573 case StyleFontFaceSourceListComponent::Tag::FormatHintString:
574 case StyleFontFaceSourceListComponent::Tag::TechFlags:
575 MOZ_ASSERT_UNREACHABLE(
576 "Should always come after a URL source, and be consumed already");
577 break;
582 if (srcArray.IsEmpty()) {
583 return nullptr;
586 return set->FindOrCreateUserFontEntry(std::move(srcArray), std::move(aAttr));
589 nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry,
590 uint32_t aSrcIndex, const char* aMessage,
591 uint32_t aFlags, nsresult aStatus) {
592 nsAutoCString familyName;
593 nsAutoCString fontURI;
594 aUserFontEntry->GetFamilyNameAndURIForLogging(aSrcIndex, familyName, fontURI);
596 nsAutoCString weightString;
597 aUserFontEntry->Weight().ToString(weightString);
598 nsAutoCString stretchString;
599 aUserFontEntry->Stretch().ToString(stretchString);
600 nsPrintfCString message(
601 "downloadable font: %s "
602 "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
603 aMessage, familyName.get(),
604 aUserFontEntry->IsItalic() ? "italic" : "normal", // XXX todo: oblique?
605 weightString.get(), stretchString.get(), aSrcIndex);
607 if (NS_FAILED(aStatus)) {
608 message.AppendLiteral(": ");
609 switch (aStatus) {
610 case NS_ERROR_DOM_BAD_URI:
611 message.AppendLiteral("bad URI or cross-site access not allowed");
612 break;
613 case NS_ERROR_CONTENT_BLOCKED:
614 message.AppendLiteral("content blocked");
615 break;
616 default:
617 message.AppendLiteral("status=");
618 message.AppendInt(static_cast<uint32_t>(aStatus));
619 break;
622 message.AppendLiteral(" source: ");
623 message.Append(fontURI);
625 LOG(("userfonts (%p) %s", this, message.get()));
627 if (GetCurrentThreadWorkerPrivate()) {
628 // TODO(aosmond): Log to the console for workers. See bug 1778537.
629 return NS_OK;
632 nsCOMPtr<nsIConsoleService> console(
633 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
634 if (!console) {
635 return NS_ERROR_NOT_AVAILABLE;
638 // try to give the user an indication of where the rule came from
639 StyleLockedFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry);
640 nsString href;
641 nsAutoCString text;
642 uint32_t line = 0;
643 uint32_t column = 0;
644 if (rule) {
645 Servo_FontFaceRule_GetCssText(rule, &text);
646 Servo_FontFaceRule_GetSourceLocation(rule, &line, &column);
647 // FIXME We need to figure out an approach to get the style sheet
648 // of this raw rule. See bug 1450903.
649 #if 0
650 StyleSheet* sheet = rule->GetStyleSheet();
651 // if the style sheet is removed while the font is loading can be null
652 if (sheet) {
653 nsCString spec = sheet->GetSheetURI()->GetSpecOrDefault();
654 CopyUTF8toUTF16(spec, href);
655 } else {
656 NS_WARNING("null parent stylesheet for @font-face rule");
657 href.AssignLiteral("unknown");
659 #endif
660 // Leave href empty if we don't know how to get the correct sheet.
663 nsresult rv;
664 nsCOMPtr<nsIScriptError> scriptError =
665 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
666 NS_ENSURE_SUCCESS(rv, rv);
668 rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message),
669 href, // file
670 NS_ConvertUTF8toUTF16(text), // src line
671 line, column,
672 aFlags, // flags
673 "CSS Loader", // category (make separate?)
674 GetInnerWindowID());
675 if (NS_SUCCEEDED(rv)) {
676 console->LogMessage(scriptError);
679 return NS_OK;
682 nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad,
683 const gfxFontFaceSrc* aFontFaceSrc,
684 uint8_t*& aBuffer,
685 uint32_t& aBufferLength) {
686 nsCOMPtr<nsIChannel> channel;
687 nsresult rv = CreateChannelForSyncLoadFontData(getter_AddRefs(channel),
688 aFontToLoad, aFontFaceSrc);
689 NS_ENSURE_SUCCESS(rv, rv);
691 // blocking stream is OK for data URIs
692 nsCOMPtr<nsIInputStream> stream;
693 rv = channel->Open(getter_AddRefs(stream));
694 NS_ENSURE_SUCCESS(rv, rv);
696 uint64_t bufferLength64;
697 rv = stream->Available(&bufferLength64);
698 NS_ENSURE_SUCCESS(rv, rv);
699 if (bufferLength64 == 0) {
700 return NS_ERROR_FAILURE;
702 if (bufferLength64 > UINT32_MAX) {
703 return NS_ERROR_FILE_TOO_BIG;
705 aBufferLength = static_cast<uint32_t>(bufferLength64);
707 // read all the decoded data
708 aBuffer = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * aBufferLength));
709 if (!aBuffer) {
710 aBufferLength = 0;
711 return NS_ERROR_OUT_OF_MEMORY;
714 uint32_t numRead, totalRead = 0;
715 while (NS_SUCCEEDED(
716 rv = stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
717 aBufferLength - totalRead, &numRead)) &&
718 numRead != 0) {
719 totalRead += numRead;
720 if (totalRead > aBufferLength) {
721 rv = NS_ERROR_FAILURE;
722 break;
726 // make sure there's a mime type
727 if (NS_SUCCEEDED(rv)) {
728 nsAutoCString mimeType;
729 rv = channel->GetContentType(mimeType);
730 aBufferLength = totalRead;
733 if (NS_FAILED(rv)) {
734 free(aBuffer);
735 aBuffer = nullptr;
736 aBufferLength = 0;
737 return rv;
740 return NS_OK;
743 void FontFaceSetImpl::OnFontFaceStatusChanged(FontFaceImpl* aFontFace) {
744 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
745 RecursiveMutexAutoLock lock(mMutex);
746 MOZ_ASSERT(HasAvailableFontFace(aFontFace));
748 mHasLoadingFontFacesIsDirty = true;
750 if (aFontFace->Status() == FontFaceLoadStatus::Loading) {
751 CheckLoadingStarted();
752 } else {
753 MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded ||
754 aFontFace->Status() == FontFaceLoadStatus::Error);
755 // When a font finishes downloading, nsPresContext::UserFontSetUpdated
756 // will be called immediately afterwards to request a reflow of the
757 // relevant elements in the document. We want to wait until the reflow
758 // request has been done before the FontFaceSet is marked as Loaded so
759 // that we don't briefly set the FontFaceSet to Loaded and then Loading
760 // again once the reflow is pending. So we go around the event loop
761 // and call CheckLoadingFinished() after the reflow has been queued.
762 if (!mDelayedLoadCheck) {
763 mDelayedLoadCheck = true;
764 DispatchCheckLoadingFinishedAfterDelay();
769 void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() {
770 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
772 if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) {
773 // See comments in Gecko_GetFontMetrics.
775 // We can't just dispatch the runnable below if we're not on the main
776 // thread, since it needs to take a strong reference to the FontFaceSet,
777 // and being a DOM object, FontFaceSet doesn't support thread-safe
778 // refcounting.
779 set->AppendTask(
780 PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay(
781 this));
782 return;
785 DispatchToOwningThread(
786 "FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay",
787 [self = RefPtr{this}]() { self->CheckLoadingFinishedAfterDelay(); });
790 void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() {
791 RecursiveMutexAutoLock lock(mMutex);
792 mDelayedLoadCheck = false;
793 CheckLoadingFinished();
796 void FontFaceSetImpl::CheckLoadingStarted() {
797 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
798 RecursiveMutexAutoLock lock(mMutex);
800 if (!HasLoadingFontFaces()) {
801 return;
804 if (mStatus == FontFaceSetLoadStatus::Loading) {
805 // We have already dispatched a loading event and replaced mReady
806 // with a fresh, unresolved promise.
807 return;
810 mStatus = FontFaceSetLoadStatus::Loading;
812 if (IsOnOwningThread()) {
813 OnLoadingStarted();
814 return;
817 DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted",
818 [self = RefPtr{this}]() { self->OnLoadingStarted(); });
821 void FontFaceSetImpl::OnLoadingStarted() {
822 RecursiveMutexAutoLock lock(mMutex);
823 if (mOwner) {
824 mOwner->DispatchLoadingEventAndReplaceReadyPromise();
828 void FontFaceSetImpl::UpdateHasLoadingFontFaces() {
829 RecursiveMutexAutoLock lock(mMutex);
830 mHasLoadingFontFacesIsDirty = false;
831 mHasLoadingFontFaces = false;
832 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
833 if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) {
834 mHasLoadingFontFaces = true;
835 return;
840 bool FontFaceSetImpl::HasLoadingFontFaces() {
841 RecursiveMutexAutoLock lock(mMutex);
842 if (mHasLoadingFontFacesIsDirty) {
843 UpdateHasLoadingFontFaces();
845 return mHasLoadingFontFaces;
848 bool FontFaceSetImpl::MightHavePendingFontLoads() {
849 // Check for FontFace objects in the FontFaceSet that are still loading.
850 return HasLoadingFontFaces();
853 void FontFaceSetImpl::CheckLoadingFinished() {
854 RecursiveMutexAutoLock lock(mMutex);
855 if (mDelayedLoadCheck) {
856 // Wait until the runnable posted in OnFontFaceStatusChanged calls us.
857 return;
860 if (!ReadyPromiseIsPending()) {
861 // We've already resolved mReady (or set the flag to do that lazily) and
862 // dispatched the loadingdone/loadingerror events.
863 return;
866 if (MightHavePendingFontLoads()) {
867 // We're not finished loading yet.
868 return;
871 mStatus = FontFaceSetLoadStatus::Loaded;
873 if (IsOnOwningThread()) {
874 OnLoadingFinished();
875 return;
878 DispatchToOwningThread(
879 "FontFaceSetImpl::CheckLoadingFinished",
880 [self = RefPtr{this}]() { self->OnLoadingFinished(); });
883 void FontFaceSetImpl::OnLoadingFinished() {
884 RecursiveMutexAutoLock lock(mMutex);
885 if (mOwner) {
886 mOwner->MaybeResolve();
890 void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() {
891 RecursiveMutexAutoLock lock(mMutex);
892 mAllowedFontLoads.Clear();
893 IncrementGenerationLocked(false);
896 // -- gfxUserFontSet
897 // ------------------------------------------------
899 already_AddRefed<gfxFontSrcPrincipal>
900 FontFaceSetImpl::GetStandardFontLoadPrincipal() const {
901 RecursiveMutexAutoLock lock(mMutex);
902 return RefPtr{mStandardFontLoadPrincipal}.forget();
905 void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize,
906 TimeStamp aDoneTime) {
907 mDownloadCount++;
908 mDownloadSize += aFontSize;
909 Telemetry::Accumulate(Telemetry::WEBFONT_SIZE, aFontSize / 1024);
911 TimeStamp navStart = GetNavigationStartTimeStamp();
912 TimeStamp zero;
913 if (navStart != zero) {
914 mozilla::glean::network::font_download_end.AccumulateRawDuration(aDoneTime -
915 navStart);
919 void FontFaceSetImpl::DoRebuildUserFontSet() { MarkUserFontSetDirty(); }
921 already_AddRefed<gfxUserFontEntry> FontFaceSetImpl::CreateUserFontEntry(
922 nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList,
923 gfxUserFontAttributes&& aAttr) {
924 RefPtr<gfxUserFontEntry> entry = new FontFaceImpl::Entry(
925 this, std::move(aFontFaceSrcList), std::move(aAttr));
926 return entry.forget();
929 void FontFaceSetImpl::ForgetLocalFaces() {
930 // We cannot hold our lock at the same time as the gfxUserFontFamily lock, so
931 // we need to make a copy of the table first.
932 nsTArray<RefPtr<gfxUserFontFamily>> fontFamilies;
934 RecursiveMutexAutoLock lock(mMutex);
935 fontFamilies.SetCapacity(mFontFamilies.Count());
936 for (const auto& fam : mFontFamilies.Values()) {
937 fontFamilies.AppendElement(fam);
941 for (const auto& fam : fontFamilies) {
942 ForgetLocalFace(fam);
946 already_AddRefed<gfxUserFontFamily> FontFaceSetImpl::GetFamily(
947 const nsACString& aFamilyName) {
948 RecursiveMutexAutoLock lock(mMutex);
949 return gfxUserFontSet::GetFamily(aFamilyName);
952 #undef LOG_ENABLED
953 #undef LOG