FontFaceSet.cpp (15085B)
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 "FontFaceSet.h" 8 9 #include "FontPreloader.h" 10 #include "ReferrerInfo.h" 11 #include "gfxFontConstants.h" 12 #include "gfxFontSrcPrincipal.h" 13 #include "gfxFontSrcURI.h" 14 #include "gfxFontUtils.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/css/Loader.h" 29 #include "mozilla/dom/CSSFontFaceRule.h" 30 #include "mozilla/dom/Document.h" 31 #include "mozilla/dom/DocumentInlines.h" 32 #include "mozilla/dom/Event.h" 33 #include "mozilla/dom/FontFaceImpl.h" 34 #include "mozilla/dom/FontFaceSetBinding.h" 35 #include "mozilla/dom/FontFaceSetDocumentImpl.h" 36 #include "mozilla/dom/FontFaceSetIterator.h" 37 #include "mozilla/dom/FontFaceSetLoadEvent.h" 38 #include "mozilla/dom/FontFaceSetLoadEventBinding.h" 39 #include "mozilla/dom/FontFaceSetWorkerImpl.h" 40 #include "mozilla/dom/Promise.h" 41 #include "nsComponentManagerUtils.h" 42 #include "nsContentPolicyUtils.h" 43 #include "nsContentUtils.h" 44 #include "nsDOMNavigationTiming.h" 45 #include "nsDeviceContext.h" 46 #include "nsFontFaceLoader.h" 47 #include "nsIConsoleService.h" 48 #include "nsIContentPolicy.h" 49 #include "nsIDocShell.h" 50 #include "nsIInputStream.h" 51 #include "nsILoadContext.h" 52 #include "nsIPrincipal.h" 53 #include "nsIWebNavigation.h" 54 #include "nsLayoutUtils.h" 55 #include "nsNetUtil.h" 56 #include "nsPresContext.h" 57 #include "nsPrintfCString.h" 58 #include "nsUTF8Utils.h" 59 60 using namespace mozilla; 61 using namespace mozilla::css; 62 using namespace mozilla::dom; 63 64 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet) 65 66 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, 67 DOMEventTargetHelper) 68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mImpl->GetDocument()); 69 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady); 70 for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) { 71 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace); 72 } 73 for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) { 74 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace); 75 } 76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 77 78 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, 79 DOMEventTargetHelper) 80 tmp->Destroy(); 81 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady); 82 for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) { 83 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace); 84 } 85 for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) { 86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace); 87 } 88 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 89 90 NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper) 91 NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper) 92 93 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet) 94 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 95 96 FontFaceSet::FontFaceSet(nsIGlobalObject* aParent) 97 : DOMEventTargetHelper(aParent) {} 98 99 FontFaceSet::~FontFaceSet() { 100 // Assert that we don't drop any FontFaceSet objects during a Servo traversal, 101 // since PostTraversalTask objects can hold raw pointers to FontFaceSets. 102 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal()); 103 104 Destroy(); 105 } 106 107 /* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForDocument( 108 dom::Document* aDocument) { 109 RefPtr<FontFaceSet> set = new FontFaceSet(aDocument->GetScopeObject()); 110 RefPtr<FontFaceSetDocumentImpl> impl = 111 new FontFaceSetDocumentImpl(set, aDocument); 112 set->mImpl = impl; 113 impl->Initialize(); 114 return set.forget(); 115 } 116 117 /* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForWorker( 118 nsIGlobalObject* aParent, WorkerPrivate* aWorkerPrivate) { 119 RefPtr<FontFaceSet> set = new FontFaceSet(aParent); 120 RefPtr<FontFaceSetWorkerImpl> impl = new FontFaceSetWorkerImpl(set); 121 set->mImpl = impl; 122 if (NS_WARN_IF(!impl->Initialize(aWorkerPrivate))) { 123 return nullptr; 124 } 125 return set.forget(); 126 } 127 128 JSObject* FontFaceSet::WrapObject(JSContext* aContext, 129 JS::Handle<JSObject*> aGivenProto) { 130 return FontFaceSet_Binding::Wrap(aContext, this, aGivenProto); 131 } 132 133 void FontFaceSet::Destroy() { mImpl->Destroy(); } 134 135 already_AddRefed<Promise> FontFaceSet::Load(JSContext* aCx, 136 const nsACString& aFont, 137 const nsAString& aText, 138 ErrorResult& aRv) { 139 FlushUserFontSet(); 140 141 nsTArray<RefPtr<Promise>> promises; 142 143 nsTArray<RefPtr<FontFace>> faces; 144 { 145 nsTArray<FontFace*> weakFaces; 146 mImpl->FindMatchingFontFaces(aFont, aText, weakFaces, aRv); 147 if (aRv.Failed()) { 148 return nullptr; 149 } 150 if (!faces.AppendElements(weakFaces, fallible) || 151 !promises.SetCapacity(weakFaces.Length(), fallible)) { 152 aRv.Throw(NS_ERROR_FAILURE); 153 return nullptr; 154 } 155 } 156 157 for (FontFace* f : faces) { 158 RefPtr<Promise> promise = f->Load(aRv); 159 if (aRv.Failed()) { 160 return nullptr; 161 } 162 promises.AppendElement(promise); 163 } 164 165 return Promise::All(aCx, promises, aRv); 166 } 167 168 bool FontFaceSet::Check(const nsACString& aFont, const nsAString& aText, 169 ErrorResult& aRv) { 170 FlushUserFontSet(); 171 172 nsTArray<FontFace*> faces; 173 mImpl->FindMatchingFontFaces(aFont, aText, faces, aRv); 174 if (aRv.Failed()) { 175 return false; 176 } 177 178 for (FontFace* f : faces) { 179 if (f->Status() != FontFaceLoadStatus::Loaded) { 180 return false; 181 } 182 } 183 184 return true; 185 } 186 187 bool FontFaceSet::ReadyPromiseIsPending() const { 188 return mReady ? mReady->State() == Promise::PromiseState::Pending 189 : !mResolveLazilyCreatedReadyPromise; 190 } 191 192 Promise* FontFaceSet::GetReady(ErrorResult& aRv) { 193 mImpl->EnsureReady(); 194 195 if (!mReady) { 196 nsCOMPtr<nsIGlobalObject> global = GetParentObject(); 197 mReady = Promise::Create(global, aRv); 198 if (!mReady) { 199 aRv.Throw(NS_ERROR_FAILURE); 200 return nullptr; 201 } 202 if (mResolveLazilyCreatedReadyPromise) { 203 mReady->MaybeResolve(this); 204 mResolveLazilyCreatedReadyPromise = false; 205 } 206 } 207 208 return mReady; 209 } 210 211 FontFaceSetLoadStatus FontFaceSet::Status() { return mImpl->Status(); } 212 213 #ifdef DEBUG 214 bool FontFaceSet::HasRuleFontFace(FontFace* aFontFace) { 215 for (size_t i = 0; i < mRuleFaces.Length(); i++) { 216 if (mRuleFaces[i].mFontFace == aFontFace) { 217 return true; 218 } 219 } 220 return false; 221 } 222 #endif 223 224 void FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv) { 225 FontFaceImpl* fontImpl = aFontFace.GetImpl(); 226 MOZ_ASSERT(fontImpl); 227 228 if (!mImpl->Add(fontImpl, aRv)) { 229 return; 230 } 231 232 MOZ_ASSERT(!aRv.Failed()); 233 234 #ifdef DEBUG 235 for (const FontFaceRecord& rec : mNonRuleFaces) { 236 MOZ_ASSERT(rec.mFontFace != &aFontFace, 237 "FontFace should not occur in mNonRuleFaces twice"); 238 } 239 #endif 240 241 FontFaceRecord* rec = mNonRuleFaces.AppendElement(); 242 rec->mFontFace = &aFontFace; 243 rec->mOrigin = Nothing(); 244 rec->mLoadEventShouldFire = 245 fontImpl->Status() == FontFaceLoadStatus::Unloaded || 246 fontImpl->Status() == FontFaceLoadStatus::Loading; 247 } 248 249 void FontFaceSet::Clear() { 250 nsTArray<FontFaceRecord> oldRecords = std::move(mNonRuleFaces); 251 mImpl->Clear(); 252 } 253 254 bool FontFaceSet::Delete(FontFace& aFontFace) { 255 // Hold onto a strong reference to make sure that when we remove FontFace from 256 // the list, the FontFaceImpl does not get freed right away. We need to check 257 // the FontFaceSetImpl first. 258 RefPtr<FontFaceImpl> fontImpl = aFontFace.GetImpl(); 259 MOZ_ASSERT(fontImpl); 260 261 // Ensure that we remove from mNonRuleFaces first. This is important so that 262 // when we check to see if all of the fonts have finished loading, the list in 263 // FontFaceSet and FontFaceSetImpl match. 264 bool removed = false; 265 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) { 266 if (mNonRuleFaces[i].mFontFace == &aFontFace) { 267 mNonRuleFaces.RemoveElementAt(i); 268 removed = true; 269 break; 270 } 271 } 272 273 if (!mImpl->Delete(fontImpl)) { 274 MOZ_ASSERT(!removed, "Missing rule present in Impl!"); 275 } else { 276 MOZ_ASSERT(removed, "Rule present but missing in Impl!"); 277 } 278 279 return removed; 280 } 281 282 bool FontFaceSet::HasAvailableFontFace(FontFace* aFontFace) { 283 return aFontFace->GetImpl()->IsInFontFaceSet(mImpl); 284 } 285 286 bool FontFaceSet::Has(FontFace& aFontFace) { 287 FlushUserFontSet(); 288 289 return HasAvailableFontFace(&aFontFace); 290 } 291 292 FontFace* FontFaceSet::GetFontFaceAt(uint32_t aIndex) { 293 FlushUserFontSet(); 294 295 if (aIndex < mRuleFaces.Length()) { 296 auto& entry = mRuleFaces[aIndex]; 297 if (entry.mOrigin.value() != StyleOrigin::Author) { 298 return nullptr; 299 } 300 return entry.mFontFace; 301 } 302 303 aIndex -= mRuleFaces.Length(); 304 if (aIndex < mNonRuleFaces.Length()) { 305 return mNonRuleFaces[aIndex].mFontFace; 306 } 307 308 return nullptr; 309 } 310 311 uint32_t FontFaceSet::Size() { 312 FlushUserFontSet(); 313 314 // Web IDL objects can only expose array index properties up to INT32_MAX. 315 316 size_t total = mNonRuleFaces.Length(); 317 for (const auto& entry : mRuleFaces) { 318 if (entry.mOrigin.value() == StyleOrigin::Author) { 319 ++total; 320 } 321 } 322 return std::min<size_t>(total, INT32_MAX); 323 } 324 325 uint32_t FontFaceSet::SizeIncludingNonAuthorOrigins() { 326 FlushUserFontSet(); 327 328 // Web IDL objects can only expose array index properties up to INT32_MAX. 329 330 size_t total = mRuleFaces.Length() + mNonRuleFaces.Length(); 331 return std::min<size_t>(total, INT32_MAX); 332 } 333 334 already_AddRefed<FontFaceSetIterator> FontFaceSet::Entries() { 335 RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, true); 336 return it.forget(); 337 } 338 339 already_AddRefed<FontFaceSetIterator> FontFaceSet::Values() { 340 RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, false); 341 return it.forget(); 342 } 343 344 void FontFaceSet::ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback, 345 JS::Handle<JS::Value> aThisArg, ErrorResult& aRv) { 346 JS::Rooted<JS::Value> thisArg(aCx, aThisArg); 347 for (size_t i = 0; i < SizeIncludingNonAuthorOrigins(); i++) { 348 RefPtr<FontFace> face = GetFontFaceAt(i); 349 if (!face) { 350 // The font at index |i| is a non-Author origin font, which we shouldn't 351 // expose per spec. 352 continue; 353 } 354 aCallback.Call(thisArg, *face, *face, *this, aRv); 355 if (aRv.Failed()) { 356 return; 357 } 358 } 359 } 360 361 bool FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules) { 362 // The impl object handles the callbacks for recreating the mRulesFaces array. 363 nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces); 364 return mImpl->UpdateRules(aRules); 365 } 366 367 void FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin) { 368 MOZ_ASSERT(!HasRuleFontFace(aFontFace)); 369 370 FontFaceRecord* rec = mRuleFaces.AppendElement(); 371 rec->mFontFace = aFontFace; 372 rec->mOrigin = Some(aOrigin); 373 rec->mLoadEventShouldFire = 374 aFontFace->Status() == FontFaceLoadStatus::Unloaded || 375 aFontFace->Status() == FontFaceLoadStatus::Loading; 376 } 377 378 void FontFaceSet::DidRefresh() { mImpl->CheckLoadingFinished(); } 379 380 void FontFaceSet::DispatchLoadingEventAndReplaceReadyPromise() { 381 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked(); 382 383 if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) { 384 // See comments in Gecko_GetFontMetrics. 385 // 386 // We can't just dispatch the runnable below if we're not on the main 387 // thread, since it needs to take a strong reference to the FontFaceSet, 388 // and being a DOM object, FontFaceSet doesn't support thread-safe 389 // refcounting. (Also, the Promise object creation must be done on 390 // the main thread.) 391 set->AppendTask( 392 PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this)); 393 return; 394 } 395 396 (new AsyncEventDispatcher(this, u"loading"_ns, CanBubble::eNo)) 397 ->PostDOMEvent(); 398 399 if (mReady && mReady->State() != Promise::PromiseState::Pending) { 400 if (GetParentObject()) { 401 ErrorResult rv; 402 mReady = Promise::Create(GetParentObject(), rv); 403 } 404 } 405 406 // We may previously have been in a state where all fonts had finished 407 // loading and we'd set mResolveLazilyCreatedReadyPromise to make sure that 408 // if we lazily create mReady for a consumer that we resolve it before 409 // returning it. We're now loading fonts, so we need to clear that flag. 410 mResolveLazilyCreatedReadyPromise = false; 411 } 412 413 void FontFaceSet::MaybeResolve() { 414 if (mReady) { 415 mReady->MaybeResolve(this); 416 } else { 417 mResolveLazilyCreatedReadyPromise = true; 418 } 419 420 // Now dispatch the loadingdone/loadingerror events. 421 nsTArray<OwningNonNull<FontFace>> loaded; 422 nsTArray<OwningNonNull<FontFace>> failed; 423 424 auto checkStatus = [&](nsTArray<FontFaceRecord>& faces) -> void { 425 for (auto& face : faces) { 426 if (!face.mLoadEventShouldFire) { 427 continue; 428 } 429 FontFace* f = face.mFontFace; 430 switch (f->Status()) { 431 case FontFaceLoadStatus::Unloaded: 432 break; 433 case FontFaceLoadStatus::Loaded: 434 loaded.AppendElement(*f); 435 face.mLoadEventShouldFire = false; 436 break; 437 case FontFaceLoadStatus::Error: 438 failed.AppendElement(*f); 439 face.mLoadEventShouldFire = false; 440 break; 441 case FontFaceLoadStatus::Loading: 442 // We should've returned above at MightHavePendingFontLoads()! 443 MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus"); 444 break; 445 } 446 } 447 }; 448 449 checkStatus(mRuleFaces); 450 checkStatus(mNonRuleFaces); 451 452 DispatchLoadingFinishedEvent(u"loadingdone"_ns, std::move(loaded)); 453 454 if (!failed.IsEmpty()) { 455 DispatchLoadingFinishedEvent(u"loadingerror"_ns, std::move(failed)); 456 } 457 } 458 459 void FontFaceSet::DispatchLoadingFinishedEvent( 460 const nsAString& aType, nsTArray<OwningNonNull<FontFace>>&& aFontFaces) { 461 FontFaceSetLoadEventInit init; 462 init.mBubbles = false; 463 init.mCancelable = false; 464 init.mFontfaces = std::move(aFontFaces); 465 RefPtr<FontFaceSetLoadEvent> event = 466 FontFaceSetLoadEvent::Constructor(this, aType, init); 467 (new AsyncEventDispatcher(this, event.forget()))->PostDOMEvent(); 468 } 469 470 void FontFaceSet::FlushUserFontSet() { mImpl->FlushUserFontSet(); } 471 472 void FontFaceSet::RefreshStandardFontLoadPrincipal() { 473 MOZ_ASSERT(NS_IsMainThread()); 474 mImpl->RefreshStandardFontLoadPrincipal(); 475 } 476 477 void FontFaceSet::CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const { 478 for (const FontFaceRecord& rec : mNonRuleFaces) { 479 IgnoredErrorResult rv; 480 RefPtr<FontFace> f = rec.mFontFace; 481 aFontFaceSet->Add(*f, rv); 482 MOZ_ASSERT(!rv.Failed()); 483 } 484 } 485 486 #undef LOG_ENABLED 487 #undef LOG