FontFaceSetImpl.cpp (30506B)
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/. */ 6 7 #include "FontFaceSetImpl.h" 8 9 #include "ReferrerInfo.h" 10 #include "gfxFontConstants.h" 11 #include "gfxFontSrcPrincipal.h" 12 #include "gfxFontSrcURI.h" 13 #include "gfxFontUtils.h" 14 #include "gfxPlatformFontList.h" 15 #include "mozilla/AsyncEventDispatcher.h" 16 #include "mozilla/BasePrincipal.h" 17 #include "mozilla/FontPropertyTypes.h" 18 #include "mozilla/LoadInfo.h" 19 #include "mozilla/Logging.h" 20 #include "mozilla/Preferences.h" 21 #include "mozilla/PresShell.h" 22 #include "mozilla/PresShellInlines.h" 23 #include "mozilla/ServoBindings.h" 24 #include "mozilla/ServoCSSParser.h" 25 #include "mozilla/ServoStyleSet.h" 26 #include "mozilla/ServoUtils.h" 27 #include "mozilla/Sprintf.h" 28 #include "mozilla/StaticPrefs_layout.h" 29 #include "mozilla/css/Loader.h" 30 #include "mozilla/dom/CSSFontFaceRule.h" 31 #include "mozilla/dom/DocumentInlines.h" 32 #include "mozilla/dom/Event.h" 33 #include "mozilla/dom/FontFaceImpl.h" 34 #include "mozilla/dom/FontFaceSet.h" 35 #include "mozilla/dom/FontFaceSetBinding.h" 36 #include "mozilla/dom/FontFaceSetLoadEvent.h" 37 #include "mozilla/dom/FontFaceSetLoadEventBinding.h" 38 #include "mozilla/dom/Promise.h" 39 #include "mozilla/dom/WorkerCommon.h" 40 #include "mozilla/dom/WorkerRunnable.h" 41 #include "mozilla/glean/GfxMetrics.h" 42 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 43 #include "nsComponentManagerUtils.h" 44 #include "nsContentUtils.h" 45 #include "nsDOMNavigationTiming.h" 46 #include "nsDeviceContext.h" 47 #include "nsFontFaceLoader.h" 48 #include "nsIConsoleService.h" 49 #include "nsIContentPolicy.h" 50 #include "nsIDocShell.h" 51 #include "nsIInputStream.h" 52 #include "nsILoadContext.h" 53 #include "nsIPrincipal.h" 54 #include "nsIWebNavigation.h" 55 #include "nsLayoutUtils.h" 56 #include "nsNetUtil.h" 57 #include "nsPresContext.h" 58 #include "nsPrintfCString.h" 59 #include "nsUTF8Utils.h" 60 61 using namespace mozilla; 62 using namespace mozilla::css; 63 using namespace mozilla::dom; 64 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) 69 70 NS_IMPL_ISUPPORTS0(FontFaceSetImpl) 71 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) {} 81 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()); 86 87 Destroy(); 88 } 89 90 void FontFaceSetImpl::DestroyLoaders() { 91 mMutex.AssertCurrentThreadIn(); 92 if (mLoaders.IsEmpty()) { 93 return; 94 } 95 if (NS_IsMainThread()) { 96 for (const auto& key : mLoaders.Keys()) { 97 key->Cancel(); 98 } 99 mLoaders.Clear(); 100 return; 101 } 102 103 class DestroyLoadersRunnable final : public Runnable { 104 public: 105 explicit DestroyLoadersRunnable(FontFaceSetImpl* aFontFaceSet) 106 : Runnable("FontFaceSetImpl::DestroyLoaders"), 107 mFontFaceSet(aFontFaceSet) {} 108 109 protected: 110 ~DestroyLoadersRunnable() override = default; 111 112 NS_IMETHOD Run() override { 113 RecursiveMutexAutoLock lock(mFontFaceSet->mMutex); 114 mFontFaceSet->DestroyLoaders(); 115 return NS_OK; 116 } 117 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; 121 }; 122 123 auto runnable = MakeRefPtr<DestroyLoadersRunnable>(this); 124 NS_DispatchToMainThread(runnable); 125 } 126 127 void FontFaceSetImpl::Destroy() { 128 nsTArray<FontFaceRecord> nonRuleFaces; 129 nsRefPtrHashtable<nsCStringHashKey, gfxUserFontFamily> fontFamilies; 130 131 { 132 RecursiveMutexAutoLock lock(mMutex); 133 DestroyLoaders(); 134 nonRuleFaces = std::move(mNonRuleFaces); 135 fontFamilies = std::move(mFontFamilies); 136 mOwner = nullptr; 137 } 138 139 if (gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList()) { 140 fp->RemoveUserFontSet(this); 141 } 142 } 143 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; 152 } 153 154 if (!ServoCSSParser::ParseFontShorthandForMatching( 155 aFont, url, aFamilyList, aStyle, aStretch, aWeight)) { 156 aRv.ThrowSyntaxError("Invalid font shorthand"); 157 return; 158 } 159 } 160 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(); 165 166 while (p < end) { 167 uint32_t c = UTF16CharEnumerator::NextChar(&p, end); 168 if (aEntry->CharacterInUnicodeRange(c)) { 169 return true; 170 } 171 } 172 return false; 173 } 174 175 void FontFaceSetImpl::FindMatchingFontFaces(const nsACString& aFont, 176 const nsAString& aText, 177 nsTArray<FontFace*>& aFontFaces, 178 ErrorResult& aRv) { 179 RecursiveMutexAutoLock lock(mMutex); 180 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; 189 } 190 191 gfxFontStyle style; 192 style.style = italicStyle; 193 style.weight = weight; 194 style.stretch = stretch; 195 196 // Set of FontFaces that we want to return. 197 nsTHashSet<FontFace*> matchingFaces; 198 199 for (const StyleSingleFontFamily& fontFamilyName : familyList.list.AsSpan()) { 200 if (!fontFamilyName.IsFamilyName()) { 201 continue; 202 } 203 204 const auto& name = fontFamilyName.AsFamilyName(); 205 RefPtr<gfxFontFamily> family = 206 LookupFamily(nsAtomCString(name.name.AsAtom())); 207 208 if (!family) { 209 continue; 210 } 211 212 AutoTArray<gfxFontEntry*, 4> entries; 213 family->FindAllFontsForStyle(style, entries); 214 215 for (gfxFontEntry* e : entries) { 216 FontFaceImpl::Entry* entry = static_cast<FontFaceImpl::Entry*>(e); 217 if (HasAnyCharacterInUnicodeRange(entry, aText)) { 218 entry->FindFontFaceOwners(matchingFaces); 219 } 220 } 221 } 222 223 if (matchingFaces.IsEmpty()) { 224 return; 225 } 226 227 // Add all FontFaces in matchingFaces to aFontFaces, in the order 228 // they appear in the FontFaceSet. 229 FindMatchingFontFaces(matchingFaces, aFontFaces); 230 } 231 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); 240 } 241 } 242 } 243 244 bool FontFaceSetImpl::ReadyPromiseIsPending() const { 245 RecursiveMutexAutoLock lock(mMutex); 246 return mOwner && mOwner->ReadyPromiseIsPending(); 247 } 248 249 FontFaceSetLoadStatus FontFaceSetImpl::Status() { 250 RecursiveMutexAutoLock lock(mMutex); 251 FlushUserFontSet(); 252 return mStatus; 253 } 254 255 bool FontFaceSetImpl::Add(FontFaceImpl* aFontFace, ErrorResult& aRv) { 256 RecursiveMutexAutoLock lock(mMutex); 257 if (aFontFace->IsInFontFaceSet(this)) { 258 return false; 259 } 260 261 if (aFontFace->HasRule()) { 262 aRv.ThrowInvalidModificationError( 263 "Can't add face to FontFaceSet that comes from an @font-face rule"); 264 return false; 265 } 266 267 aFontFace->AddFontFaceSet(this); 268 269 #ifdef DEBUG 270 for (const FontFaceRecord& rec : mNonRuleFaces) { 271 MOZ_ASSERT(rec.mFontFace != aFontFace, 272 "FontFace should not occur in mNonRuleFaces twice"); 273 } 274 #endif 275 276 FontFaceRecord* rec = mNonRuleFaces.AppendElement(); 277 rec->mFontFace = aFontFace; 278 rec->mOrigin = Nothing(); 279 280 mNonRuleFacesDirty = true; 281 MarkUserFontSetDirty(); 282 mHasLoadingFontFacesIsDirty = true; 283 CheckLoadingStarted(); 284 return true; 285 } 286 287 void FontFaceSetImpl::Clear() { 288 RecursiveMutexAutoLock lock(mMutex); 289 if (mNonRuleFaces.IsEmpty()) { 290 return; 291 } 292 293 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { 294 FontFaceImpl* f = mNonRuleFaces[i].mFontFace; 295 f->RemoveFontFaceSet(this); 296 } 297 298 mNonRuleFaces.Clear(); 299 mNonRuleFacesDirty = true; 300 MarkUserFontSetDirty(); 301 mHasLoadingFontFacesIsDirty = true; 302 CheckLoadingFinished(); 303 } 304 305 bool FontFaceSetImpl::Delete(FontFaceImpl* aFontFace) { 306 RecursiveMutexAutoLock lock(mMutex); 307 if (aFontFace->HasRule()) { 308 return false; 309 } 310 311 bool removed = false; 312 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { 313 if (mNonRuleFaces[i].mFontFace == aFontFace) { 314 mNonRuleFaces.RemoveElementAt(i); 315 removed = true; 316 break; 317 } 318 } 319 if (!removed) { 320 return false; 321 } 322 323 aFontFace->RemoveFontFaceSet(this); 324 325 mNonRuleFacesDirty = true; 326 MarkUserFontSetDirty(); 327 mHasLoadingFontFacesIsDirty = true; 328 CheckLoadingFinished(); 329 return true; 330 } 331 332 bool FontFaceSetImpl::HasAvailableFontFace(FontFaceImpl* aFontFace) { 333 return aFontFace->IsInFontFaceSet(this); 334 } 335 336 void FontFaceSetImpl::RemoveLoader(nsFontFaceLoader* aLoader) { 337 RecursiveMutexAutoLock lock(mMutex); 338 mLoaders.RemoveEntry(aLoader); 339 } 340 341 void FontFaceSetImpl::InsertNonRuleFontFace(FontFaceImpl* aFontFace) { 342 gfxUserFontAttributes attr; 343 if (!aFontFace->GetAttributes(attr)) { 344 // If there is no family name, this rule cannot contribute a 345 // usable font, so there is no point in processing it further. 346 return; 347 } 348 349 nsAutoCString family(attr.mFamilyName); 350 351 // Just create a new font entry if we haven't got one already. 352 if (!aFontFace->GetUserFontEntry()) { 353 // XXX Should we be checking mLocalRulesUsed like InsertRuleFontFace does? 354 RefPtr<gfxUserFontEntry> entry = FindOrCreateUserFontEntryFromFontFace( 355 aFontFace, std::move(attr), StyleOrigin::Author); 356 if (!entry) { 357 return; 358 } 359 aFontFace->SetUserFontEntry(entry); 360 } 361 AddUserFontEntry(family, aFontFace->GetUserFontEntry()); 362 } 363 364 void FontFaceSetImpl::UpdateUserFontEntry(gfxUserFontEntry* aEntry, 365 gfxUserFontAttributes&& aAttr) { 366 MOZ_ASSERT(NS_IsMainThread()); 367 368 bool resetFamilyName = !aEntry->mFamilyName.IsEmpty() && 369 aEntry->mFamilyName != aAttr.mFamilyName; 370 // aFontFace already has a user font entry, so we update its attributes 371 // rather than creating a new one. 372 aEntry->UpdateAttributes(std::move(aAttr)); 373 // If the family name has changed, remove the entry from its current family 374 // and clear the mFamilyName field so it can be reset when added to a new 375 // family. 376 if (resetFamilyName) { 377 RefPtr<gfxUserFontFamily> family = LookupFamily(aEntry->mFamilyName); 378 if (family) { 379 family->RemoveFontEntry(aEntry); 380 } 381 aEntry->mFamilyName.Truncate(0); 382 } 383 } 384 385 class FontFaceSetImpl::UpdateUserFontEntryRunnable final 386 : public WorkerMainThreadRunnable { 387 public: 388 UpdateUserFontEntryRunnable(FontFaceSetImpl* aSet, gfxUserFontEntry* aEntry, 389 gfxUserFontAttributes& aAttr) 390 : WorkerMainThreadRunnable( 391 GetCurrentThreadWorkerPrivate(), 392 "FontFaceSetImpl :: FindOrCreateUserFontEntryFromFontFace"_ns), 393 mSet(aSet), 394 mEntry(aEntry), 395 mAttr(aAttr) {} 396 397 bool MainThreadRun() override { 398 mSet->UpdateUserFontEntry(mEntry, std::move(mAttr)); 399 return true; 400 } 401 402 private: 403 FontFaceSetImpl* mSet; 404 gfxUserFontEntry* mEntry; 405 gfxUserFontAttributes& mAttr; 406 }; 407 408 // TODO(emilio): Should this take an nsAtom* aFamilyName instead? 409 // 410 // All callers have one handy. 411 /* static */ 412 already_AddRefed<gfxUserFontEntry> 413 FontFaceSetImpl::FindOrCreateUserFontEntryFromFontFace( 414 FontFaceImpl* aFontFace, gfxUserFontAttributes&& aAttr, 415 StyleOrigin aOrigin) { 416 FontFaceSetImpl* set = aFontFace->GetPrimaryFontFaceSet(); 417 418 RefPtr<gfxUserFontEntry> existingEntry = aFontFace->GetUserFontEntry(); 419 if (existingEntry) { 420 if (NS_IsMainThread()) { 421 set->UpdateUserFontEntry(existingEntry, std::move(aAttr)); 422 } else { 423 auto task = 424 MakeRefPtr<UpdateUserFontEntryRunnable>(set, existingEntry, aAttr); 425 IgnoredErrorResult ignoredRv; 426 task->Dispatch(GetCurrentThreadWorkerPrivate(), Canceling, ignoredRv); 427 } 428 return existingEntry.forget(); 429 } 430 431 // set up src array 432 nsTArray<gfxFontFaceSrc> srcArray; 433 434 if (aFontFace->HasFontData()) { 435 gfxFontFaceSrc* face = srcArray.AppendElement(); 436 if (!face) { 437 return nullptr; 438 } 439 440 face->mSourceType = gfxFontFaceSrc::eSourceType_Buffer; 441 face->mBuffer = aFontFace->TakeBufferSource(); 442 } else { 443 size_t len = aAttr.mSources.Length(); 444 for (size_t i = 0; i < len; ++i) { 445 gfxFontFaceSrc* face = srcArray.AppendElement(); 446 const auto& component = aAttr.mSources[i]; 447 switch (component.tag) { 448 case StyleFontFaceSourceListComponent::Tag::Local: { 449 nsAtom* atom = component.AsLocal(); 450 face->mLocalName.Append(nsAtomCString(atom)); 451 face->mSourceType = gfxFontFaceSrc::eSourceType_Local; 452 face->mURI = nullptr; 453 face->mFormatHint = StyleFontFaceSourceFormatKeyword::None; 454 break; 455 } 456 457 case StyleFontFaceSourceListComponent::Tag::Url: { 458 face->mSourceType = gfxFontFaceSrc::eSourceType_URL; 459 const StyleCssUrl* url = component.AsUrl(); 460 nsIURI* uri = url->GetURI(); 461 face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr; 462 const URLExtraData& extraData = url->ExtraData(); 463 face->mReferrerInfo = extraData.ReferrerInfo(); 464 465 // agent and user stylesheets are treated slightly differently, 466 // the same-site origin check and access control headers are 467 // enforced against the sheet principal rather than the document 468 // principal to allow user stylesheets to include @font-face rules 469 if (aOrigin == StyleOrigin::User || 470 aOrigin == StyleOrigin::UserAgent) { 471 face->mUseOriginPrincipal = true; 472 face->mOriginPrincipal = new gfxFontSrcPrincipal( 473 extraData.Principal(), extraData.Principal()); 474 } 475 476 face->mLocalName.Truncate(); 477 face->mFormatHint = StyleFontFaceSourceFormatKeyword::None; 478 face->mTechFlags = StyleFontFaceSourceTechFlags::Empty(); 479 480 if (i + 1 < len) { 481 // Check for a format hint. 482 const auto& next = aAttr.mSources[i + 1]; 483 switch (next.tag) { 484 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword: 485 face->mFormatHint = next.format_hint_keyword._0; 486 i++; 487 break; 488 case StyleFontFaceSourceListComponent::Tag::FormatHintString: { 489 nsDependentCSubstring valueString( 490 reinterpret_cast<const char*>( 491 next.format_hint_string.utf8_bytes), 492 next.format_hint_string.length); 493 494 if (valueString.LowerCaseEqualsASCII("woff")) { 495 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff; 496 } else if (valueString.LowerCaseEqualsASCII("woff2")) { 497 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2; 498 } else if (valueString.LowerCaseEqualsASCII("opentype")) { 499 face->mFormatHint = 500 StyleFontFaceSourceFormatKeyword::Opentype; 501 } else if (valueString.LowerCaseEqualsASCII("truetype")) { 502 face->mFormatHint = 503 StyleFontFaceSourceFormatKeyword::Truetype; 504 } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) { 505 face->mFormatHint = 506 StyleFontFaceSourceFormatKeyword::Truetype; 507 } else if (valueString.LowerCaseEqualsASCII( 508 "embedded-opentype")) { 509 face->mFormatHint = 510 StyleFontFaceSourceFormatKeyword::EmbeddedOpentype; 511 } else if (valueString.LowerCaseEqualsASCII("svg")) { 512 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Svg; 513 } else if (StaticPrefs::layout_css_font_variations_enabled()) { 514 // Non-standard values that Firefox accepted, for back-compat; 515 // these are superseded by the tech() function. 516 if (valueString.LowerCaseEqualsASCII("woff-variations")) { 517 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff; 518 } else if (valueString.LowerCaseEqualsASCII( 519 "woff2-variations")) { 520 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Woff2; 521 } else if (valueString.LowerCaseEqualsASCII( 522 "opentype-variations")) { 523 face->mFormatHint = 524 StyleFontFaceSourceFormatKeyword::Opentype; 525 } else if (valueString.LowerCaseEqualsASCII( 526 "truetype-variations")) { 527 face->mFormatHint = 528 StyleFontFaceSourceFormatKeyword::Truetype; 529 } else { 530 face->mFormatHint = 531 StyleFontFaceSourceFormatKeyword::Unknown; 532 } 533 } else { 534 // unknown format specified, mark to distinguish from the 535 // case where no format hints are specified 536 face->mFormatHint = StyleFontFaceSourceFormatKeyword::Unknown; 537 } 538 i++; 539 break; 540 } 541 case StyleFontFaceSourceListComponent::Tag::TechFlags: 542 case StyleFontFaceSourceListComponent::Tag::Local: 543 case StyleFontFaceSourceListComponent::Tag::Url: 544 break; 545 } 546 } 547 548 if (i + 1 < len) { 549 // Check for a set of font-technologies flags. 550 const auto& next = aAttr.mSources[i + 1]; 551 if (next.IsTechFlags()) { 552 face->mTechFlags = next.AsTechFlags(); 553 i++; 554 } 555 } 556 557 if (!face->mURI) { 558 // if URI not valid, omit from src array 559 srcArray.RemoveLastElement(); 560 NS_WARNING("null url in @font-face rule"); 561 continue; 562 } 563 break; 564 } 565 566 case StyleFontFaceSourceListComponent::Tag::FormatHintKeyword: 567 case StyleFontFaceSourceListComponent::Tag::FormatHintString: 568 case StyleFontFaceSourceListComponent::Tag::TechFlags: 569 MOZ_ASSERT_UNREACHABLE( 570 "Should always come after a URL source, and be consumed already"); 571 break; 572 } 573 } 574 } 575 576 if (srcArray.IsEmpty()) { 577 return nullptr; 578 } 579 580 return set->FindOrCreateUserFontEntry(std::move(srcArray), std::move(aAttr)); 581 } 582 583 nsresult FontFaceSetImpl::LogMessage(gfxUserFontEntry* aUserFontEntry, 584 uint32_t aSrcIndex, const char* aMessage, 585 uint32_t aFlags, nsresult aStatus) { 586 nsAutoCString familyName; 587 nsAutoCString fontURI; 588 aUserFontEntry->GetFamilyNameAndURIForLogging(aSrcIndex, familyName, fontURI); 589 590 nsAutoCString weightString; 591 aUserFontEntry->Weight().ToString(weightString); 592 nsAutoCString stretchString; 593 aUserFontEntry->Stretch().ToString(stretchString); 594 nsPrintfCString message( 595 "downloadable font: %s " 596 "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)", 597 aMessage, familyName.get(), 598 aUserFontEntry->IsItalic() ? "italic" : "normal", // XXX todo: oblique? 599 weightString.get(), stretchString.get(), aSrcIndex); 600 601 if (NS_FAILED(aStatus)) { 602 message.AppendLiteral(": "); 603 switch (aStatus) { 604 case NS_ERROR_DOM_BAD_URI: 605 message.AppendLiteral("bad URI or cross-site access not allowed"); 606 break; 607 case NS_ERROR_CONTENT_BLOCKED: 608 message.AppendLiteral("content blocked"); 609 break; 610 default: 611 message.AppendLiteral("status="); 612 message.AppendInt(static_cast<uint32_t>(aStatus)); 613 break; 614 } 615 } 616 message.AppendLiteral(" source: "); 617 message.Append(fontURI); 618 619 LOG(("userfonts (%p) %s", this, message.get())); 620 621 if (GetCurrentThreadWorkerPrivate()) { 622 // TODO(aosmond): Log to the console for workers. See bug 1778537. 623 return NS_OK; 624 } 625 626 nsCOMPtr<nsIConsoleService> console( 627 do_GetService(NS_CONSOLESERVICE_CONTRACTID)); 628 if (!console) { 629 return NS_ERROR_NOT_AVAILABLE; 630 } 631 632 // try to give the user an indication of where the rule came from 633 StyleLockedFontFaceRule* rule = FindRuleForUserFontEntry(aUserFontEntry); 634 nsAutoCString href; 635 uint32_t line = 0; 636 uint32_t column = 0; 637 if (rule) { 638 Servo_FontFaceRule_GetSourceLocation(rule, &line, &column); 639 // FIXME We need to figure out an approach to get the style sheet 640 // of this raw rule. See bug 1450903. Leave href empty if we don't know how 641 // to get the correct sheet. 642 } 643 644 nsresult rv; 645 nsCOMPtr<nsIScriptError> scriptError = 646 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); 647 NS_ENSURE_SUCCESS(rv, rv); 648 649 rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(message), 650 href, // file 651 line, column, 652 aFlags, // flags 653 "CSS Loader", // category (make separate?) 654 GetInnerWindowID()); 655 if (NS_SUCCEEDED(rv)) { 656 console->LogMessage(scriptError); 657 } 658 659 return NS_OK; 660 } 661 662 nsresult FontFaceSetImpl::SyncLoadFontData(gfxUserFontEntry* aFontToLoad, 663 const gfxFontFaceSrc* aFontFaceSrc, 664 uint8_t*& aBuffer, 665 uint32_t& aBufferLength) { 666 nsCOMPtr<nsIChannel> channel; 667 nsresult rv = CreateChannelForSyncLoadFontData(getter_AddRefs(channel), 668 aFontToLoad, aFontFaceSrc); 669 NS_ENSURE_SUCCESS(rv, rv); 670 671 // blocking stream is OK for data URIs 672 nsCOMPtr<nsIInputStream> stream; 673 rv = channel->Open(getter_AddRefs(stream)); 674 NS_ENSURE_SUCCESS(rv, rv); 675 676 uint64_t bufferLength64; 677 rv = stream->Available(&bufferLength64); 678 NS_ENSURE_SUCCESS(rv, rv); 679 if (bufferLength64 == 0) { 680 return NS_ERROR_FAILURE; 681 } 682 if (bufferLength64 > UINT32_MAX) { 683 return NS_ERROR_FILE_TOO_BIG; 684 } 685 aBufferLength = static_cast<uint32_t>(bufferLength64); 686 687 // read all the decoded data 688 aBuffer = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * aBufferLength)); 689 if (!aBuffer) { 690 aBufferLength = 0; 691 return NS_ERROR_OUT_OF_MEMORY; 692 } 693 694 uint32_t numRead, totalRead = 0; 695 while (NS_SUCCEEDED( 696 rv = stream->Read(reinterpret_cast<char*>(aBuffer + totalRead), 697 aBufferLength - totalRead, &numRead)) && 698 numRead != 0) { 699 totalRead += numRead; 700 if (totalRead > aBufferLength) { 701 rv = NS_ERROR_FAILURE; 702 break; 703 } 704 } 705 706 // make sure there's a mime type 707 if (NS_SUCCEEDED(rv)) { 708 nsAutoCString mimeType; 709 rv = channel->GetContentType(mimeType); 710 aBufferLength = totalRead; 711 } 712 713 if (NS_FAILED(rv)) { 714 free(aBuffer); 715 aBuffer = nullptr; 716 aBufferLength = 0; 717 return rv; 718 } 719 720 return NS_OK; 721 } 722 723 void FontFaceSetImpl::OnFontFaceStatusChanged(FontFaceImpl* aFontFace) { 724 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); 725 RecursiveMutexAutoLock lock(mMutex); 726 MOZ_ASSERT(HasAvailableFontFace(aFontFace)); 727 728 mHasLoadingFontFacesIsDirty = true; 729 730 if (aFontFace->Status() == FontFaceLoadStatus::Loading) { 731 CheckLoadingStarted(); 732 } else { 733 MOZ_ASSERT(aFontFace->Status() == FontFaceLoadStatus::Loaded || 734 aFontFace->Status() == FontFaceLoadStatus::Error); 735 // When a font finishes downloading, nsPresContext::UserFontSetUpdated 736 // will be called immediately afterwards to request a reflow of the 737 // relevant elements in the document. We want to wait until the reflow 738 // request has been done before the FontFaceSet is marked as Loaded so 739 // that we don't briefly set the FontFaceSet to Loaded and then Loading 740 // again once the reflow is pending. So we go around the event loop 741 // and call CheckLoadingFinished() after the reflow has been queued. 742 if (!mDelayedLoadCheck) { 743 mDelayedLoadCheck = true; 744 DispatchCheckLoadingFinishedAfterDelay(); 745 } 746 } 747 } 748 749 void FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay() { 750 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); 751 752 if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) { 753 // See comments in Gecko_GetFontMetrics. 754 // 755 // We can't just dispatch the runnable below if we're not on the main 756 // thread, since it needs to take a strong reference to the FontFaceSet, 757 // and being a DOM object, FontFaceSet doesn't support thread-safe 758 // refcounting. 759 set->AppendTask( 760 PostTraversalTask::DispatchFontFaceSetCheckLoadingFinishedAfterDelay( 761 this)); 762 return; 763 } 764 765 DispatchToOwningThread( 766 "FontFaceSetImpl::DispatchCheckLoadingFinishedAfterDelay", 767 [self = RefPtr{this}]() { self->CheckLoadingFinishedAfterDelay(); }); 768 } 769 770 void FontFaceSetImpl::CheckLoadingFinishedAfterDelay() { 771 RecursiveMutexAutoLock lock(mMutex); 772 mDelayedLoadCheck = false; 773 CheckLoadingFinished(); 774 } 775 776 void FontFaceSetImpl::CheckLoadingStarted() { 777 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); 778 RecursiveMutexAutoLock lock(mMutex); 779 780 if (!HasLoadingFontFaces()) { 781 return; 782 } 783 784 if (mStatus == FontFaceSetLoadStatus::Loading) { 785 // We have already dispatched a loading event and replaced mReady 786 // with a fresh, unresolved promise. 787 return; 788 } 789 790 mStatus = FontFaceSetLoadStatus::Loading; 791 792 if (IsOnOwningThread()) { 793 OnLoadingStarted(); 794 return; 795 } 796 797 DispatchToOwningThread("FontFaceSetImpl::CheckLoadingStarted", 798 [self = RefPtr{this}]() { self->OnLoadingStarted(); }); 799 } 800 801 void FontFaceSetImpl::OnLoadingStarted() { 802 RecursiveMutexAutoLock lock(mMutex); 803 if (mOwner) { 804 mOwner->DispatchLoadingEventAndReplaceReadyPromise(); 805 } 806 } 807 808 void FontFaceSetImpl::UpdateHasLoadingFontFaces() { 809 RecursiveMutexAutoLock lock(mMutex); 810 mHasLoadingFontFacesIsDirty = false; 811 mHasLoadingFontFaces = false; 812 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { 813 if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) { 814 mHasLoadingFontFaces = true; 815 return; 816 } 817 } 818 } 819 820 bool FontFaceSetImpl::HasLoadingFontFaces() { 821 RecursiveMutexAutoLock lock(mMutex); 822 if (mHasLoadingFontFacesIsDirty) { 823 UpdateHasLoadingFontFaces(); 824 } 825 return mHasLoadingFontFaces; 826 } 827 828 bool FontFaceSetImpl::MightHavePendingFontLoads() { 829 // Check for FontFace objects in the FontFaceSet that are still loading. 830 return HasLoadingFontFaces(); 831 } 832 833 void FontFaceSetImpl::CheckLoadingFinished() { 834 RecursiveMutexAutoLock lock(mMutex); 835 if (mDelayedLoadCheck) { 836 // Wait until the runnable posted in OnFontFaceStatusChanged calls us. 837 return; 838 } 839 840 if (!ReadyPromiseIsPending()) { 841 // We've already resolved mReady (or set the flag to do that lazily) and 842 // dispatched the loadingdone/loadingerror events. 843 return; 844 } 845 846 if (MightHavePendingFontLoads()) { 847 // We're not finished loading yet. 848 return; 849 } 850 851 mStatus = FontFaceSetLoadStatus::Loaded; 852 853 if (IsOnOwningThread()) { 854 OnLoadingFinished(); 855 return; 856 } 857 858 DispatchToOwningThread( 859 "FontFaceSetImpl::CheckLoadingFinished", 860 [self = RefPtr{this}]() { self->OnLoadingFinished(); }); 861 } 862 863 void FontFaceSetImpl::OnLoadingFinished() { 864 RecursiveMutexAutoLock lock(mMutex); 865 if (mOwner) { 866 mOwner->MaybeResolve(); 867 } 868 } 869 870 void FontFaceSetImpl::RefreshStandardFontLoadPrincipal() { 871 RecursiveMutexAutoLock lock(mMutex); 872 mAllowedFontLoads.Clear(); 873 IncrementGenerationLocked(false); 874 } 875 876 // -- gfxUserFontSet 877 // ------------------------------------------------ 878 879 already_AddRefed<gfxFontSrcPrincipal> 880 FontFaceSetImpl::GetStandardFontLoadPrincipal() const { 881 RecursiveMutexAutoLock lock(mMutex); 882 return RefPtr{mStandardFontLoadPrincipal}.forget(); 883 } 884 885 void FontFaceSetImpl::RecordFontLoadDone(uint32_t aFontSize, 886 TimeStamp aDoneTime) { 887 mDownloadCount++; 888 mDownloadSize += aFontSize; 889 glean::webfont::size.Accumulate(aFontSize / 1024); 890 891 TimeStamp navStart = GetNavigationStartTimeStamp(); 892 TimeStamp zero; 893 if (navStart != zero) { 894 mozilla::glean::network::font_download_end.AccumulateRawDuration(aDoneTime - 895 navStart); 896 } 897 } 898 899 void FontFaceSetImpl::DoRebuildUserFontSet() { MarkUserFontSetDirty(); } 900 901 already_AddRefed<gfxUserFontEntry> FontFaceSetImpl::CreateUserFontEntry( 902 nsTArray<gfxFontFaceSrc>&& aFontFaceSrcList, 903 gfxUserFontAttributes&& aAttr) { 904 RefPtr<gfxUserFontEntry> entry = new FontFaceImpl::Entry( 905 this, std::move(aFontFaceSrcList), std::move(aAttr)); 906 return entry.forget(); 907 } 908 909 void FontFaceSetImpl::ForgetLocalFaces() { 910 // We cannot hold our lock at the same time as the gfxUserFontFamily lock, so 911 // we need to make a copy of the table first. 912 nsTArray<RefPtr<gfxUserFontFamily>> fontFamilies; 913 { 914 RecursiveMutexAutoLock lock(mMutex); 915 fontFamilies.SetCapacity(mFontFamilies.Count()); 916 for (const auto& fam : mFontFamilies.Values()) { 917 fontFamilies.AppendElement(fam); 918 } 919 } 920 921 for (const auto& fam : fontFamilies) { 922 ForgetLocalFace(fam); 923 } 924 } 925 926 already_AddRefed<gfxUserFontFamily> FontFaceSetImpl::GetFamily( 927 const nsACString& aFamilyName) { 928 RecursiveMutexAutoLock lock(mMutex); 929 return gfxUserFontSet::GetFamily(aFamilyName); 930 } 931 932 #undef LOG_ENABLED 933 #undef LOG