Bug 1879449 [wpt PR 44489] - [wptrunner] Add `infrastructure/expected-fail/` test...
[gecko.git] / layout / style / FontFaceSet.cpp
blobddc624021618c0aca5fdd26eb041402c2ee75c28
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 "FontFaceSet.h"
9 #include "gfxFontConstants.h"
10 #include "gfxFontSrcPrincipal.h"
11 #include "gfxFontSrcURI.h"
12 #include "gfxFontUtils.h"
13 #include "FontPreloader.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/FontFaceSetBinding.h"
20 #include "mozilla/dom/FontFaceSetDocumentImpl.h"
21 #include "mozilla/dom/FontFaceSetWorkerImpl.h"
22 #include "mozilla/dom/FontFaceSetIterator.h"
23 #include "mozilla/dom/FontFaceSetLoadEvent.h"
24 #include "mozilla/dom/FontFaceSetLoadEventBinding.h"
25 #include "mozilla/dom/Promise.h"
26 #include "mozilla/FontPropertyTypes.h"
27 #include "mozilla/AsyncEventDispatcher.h"
28 #include "mozilla/BasePrincipal.h"
29 #include "mozilla/Logging.h"
30 #include "mozilla/Preferences.h"
31 #include "mozilla/PresShell.h"
32 #include "mozilla/PresShellInlines.h"
33 #include "mozilla/ServoBindings.h"
34 #include "mozilla/ServoCSSParser.h"
35 #include "mozilla/ServoStyleSet.h"
36 #include "mozilla/ServoUtils.h"
37 #include "mozilla/Sprintf.h"
38 #include "mozilla/Telemetry.h"
39 #include "mozilla/LoadInfo.h"
40 #include "nsComponentManagerUtils.h"
41 #include "nsContentPolicyUtils.h"
42 #include "nsContentUtils.h"
43 #include "nsDeviceContext.h"
44 #include "nsFontFaceLoader.h"
45 #include "nsIConsoleService.h"
46 #include "nsIContentPolicy.h"
47 #include "nsIDocShell.h"
48 #include "mozilla/dom/Document.h"
49 #include "nsILoadContext.h"
50 #include "nsINetworkPredictor.h"
51 #include "nsIPrincipal.h"
52 #include "nsIWebNavigation.h"
53 #include "nsNetUtil.h"
54 #include "nsIInputStream.h"
55 #include "nsLayoutUtils.h"
56 #include "nsPresContext.h"
57 #include "nsPrintfCString.h"
58 #include "nsUTF8Utils.h"
59 #include "nsDOMNavigationTiming.h"
60 #include "ReferrerInfo.h"
62 using namespace mozilla;
63 using namespace mozilla::css;
64 using namespace mozilla::dom;
66 NS_IMPL_CYCLE_COLLECTION_CLASS(FontFaceSet)
68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet,
69 DOMEventTargetHelper)
70 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mImpl->GetDocument());
71 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReady);
72 for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
73 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);
75 for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace);
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
80 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet,
81 DOMEventTargetHelper)
82 tmp->Destroy();
83 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReady);
84 for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
85 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace);
87 for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace);
90 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
92 NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper)
93 NS_IMPL_RELEASE_INHERITED(FontFaceSet, DOMEventTargetHelper)
95 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FontFaceSet)
96 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
98 FontFaceSet::FontFaceSet(nsIGlobalObject* aParent)
99 : DOMEventTargetHelper(aParent) {}
101 FontFaceSet::~FontFaceSet() {
102 // Assert that we don't drop any FontFaceSet objects during a Servo traversal,
103 // since PostTraversalTask objects can hold raw pointers to FontFaceSets.
104 MOZ_ASSERT(!gfxFontUtils::IsInServoTraversal());
106 Destroy();
109 /* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForDocument(
110 dom::Document* aDocument) {
111 RefPtr<FontFaceSet> set = new FontFaceSet(aDocument->GetScopeObject());
112 RefPtr<FontFaceSetDocumentImpl> impl =
113 new FontFaceSetDocumentImpl(set, aDocument);
114 set->mImpl = impl;
115 impl->Initialize();
116 return set.forget();
119 /* static */ already_AddRefed<FontFaceSet> FontFaceSet::CreateForWorker(
120 nsIGlobalObject* aParent, WorkerPrivate* aWorkerPrivate) {
121 RefPtr<FontFaceSet> set = new FontFaceSet(aParent);
122 RefPtr<FontFaceSetWorkerImpl> impl = new FontFaceSetWorkerImpl(set);
123 set->mImpl = impl;
124 if (NS_WARN_IF(!impl->Initialize(aWorkerPrivate))) {
125 return nullptr;
127 return set.forget();
130 JSObject* FontFaceSet::WrapObject(JSContext* aContext,
131 JS::Handle<JSObject*> aGivenProto) {
132 return FontFaceSet_Binding::Wrap(aContext, this, aGivenProto);
135 void FontFaceSet::Destroy() { mImpl->Destroy(); }
137 already_AddRefed<Promise> FontFaceSet::Load(JSContext* aCx,
138 const nsACString& aFont,
139 const nsAString& aText,
140 ErrorResult& aRv) {
141 FlushUserFontSet();
143 nsTArray<RefPtr<Promise>> promises;
145 nsTArray<FontFace*> faces;
146 mImpl->FindMatchingFontFaces(aFont, aText, faces, aRv);
147 if (aRv.Failed()) {
148 return nullptr;
151 for (FontFace* f : faces) {
152 RefPtr<Promise> promise = f->Load(aRv);
153 if (aRv.Failed()) {
154 return nullptr;
156 if (!promises.AppendElement(promise, fallible)) {
157 aRv.Throw(NS_ERROR_FAILURE);
158 return nullptr;
162 return Promise::All(aCx, promises, aRv);
165 bool FontFaceSet::Check(const nsACString& aFont, const nsAString& aText,
166 ErrorResult& aRv) {
167 FlushUserFontSet();
169 nsTArray<FontFace*> faces;
170 mImpl->FindMatchingFontFaces(aFont, aText, faces, aRv);
171 if (aRv.Failed()) {
172 return false;
175 for (FontFace* f : faces) {
176 if (f->Status() != FontFaceLoadStatus::Loaded) {
177 return false;
181 return true;
184 bool FontFaceSet::ReadyPromiseIsPending() const {
185 return mReady ? mReady->State() == Promise::PromiseState::Pending
186 : !mResolveLazilyCreatedReadyPromise;
189 Promise* FontFaceSet::GetReady(ErrorResult& aRv) {
190 mImpl->EnsureReady();
192 if (!mReady) {
193 nsCOMPtr<nsIGlobalObject> global = GetParentObject();
194 mReady = Promise::Create(global, aRv);
195 if (!mReady) {
196 aRv.Throw(NS_ERROR_FAILURE);
197 return nullptr;
199 if (mResolveLazilyCreatedReadyPromise) {
200 mReady->MaybeResolve(this);
201 mResolveLazilyCreatedReadyPromise = false;
205 return mReady;
208 FontFaceSetLoadStatus FontFaceSet::Status() { return mImpl->Status(); }
210 #ifdef DEBUG
211 bool FontFaceSet::HasRuleFontFace(FontFace* aFontFace) {
212 for (size_t i = 0; i < mRuleFaces.Length(); i++) {
213 if (mRuleFaces[i].mFontFace == aFontFace) {
214 return true;
217 return false;
219 #endif
221 void FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv) {
222 FlushUserFontSet();
224 FontFaceImpl* fontImpl = aFontFace.GetImpl();
225 MOZ_ASSERT(fontImpl);
227 if (!mImpl->Add(fontImpl, aRv)) {
228 return;
231 MOZ_ASSERT(!aRv.Failed());
233 #ifdef DEBUG
234 for (const FontFaceRecord& rec : mNonRuleFaces) {
235 MOZ_ASSERT(rec.mFontFace != &aFontFace,
236 "FontFace should not occur in mNonRuleFaces twice");
238 #endif
240 FontFaceRecord* rec = mNonRuleFaces.AppendElement();
241 rec->mFontFace = &aFontFace;
242 rec->mOrigin = Nothing();
243 rec->mLoadEventShouldFire =
244 fontImpl->Status() == FontFaceLoadStatus::Unloaded ||
245 fontImpl->Status() == FontFaceLoadStatus::Loading;
248 void FontFaceSet::Clear() {
249 nsTArray<FontFaceRecord> oldRecords = std::move(mNonRuleFaces);
250 mImpl->Clear();
253 bool FontFaceSet::Delete(FontFace& aFontFace) {
254 // Hold onto a strong reference to make sure that when we remove FontFace from
255 // the list, the FontFaceImpl does not get freed right away. We need to check
256 // the FontFaceSetImpl first.
257 RefPtr<FontFaceImpl> fontImpl = aFontFace.GetImpl();
258 MOZ_ASSERT(fontImpl);
260 // Ensure that we remove from mNonRuleFaces first. This is important so that
261 // when we check to see if all of the fonts have finished loading, the list in
262 // FontFaceSet and FontFaceSetImpl match.
263 bool removed = false;
264 for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
265 if (mNonRuleFaces[i].mFontFace == &aFontFace) {
266 mNonRuleFaces.RemoveElementAt(i);
267 removed = true;
268 break;
272 if (!mImpl->Delete(fontImpl)) {
273 MOZ_ASSERT(!removed, "Missing rule present in Impl!");
274 } else {
275 MOZ_ASSERT(removed, "Rule present but missing in Impl!");
278 return removed;
281 bool FontFaceSet::HasAvailableFontFace(FontFace* aFontFace) {
282 return aFontFace->GetImpl()->IsInFontFaceSet(mImpl);
285 bool FontFaceSet::Has(FontFace& aFontFace) {
286 FlushUserFontSet();
288 return HasAvailableFontFace(&aFontFace);
291 FontFace* FontFaceSet::GetFontFaceAt(uint32_t aIndex) {
292 FlushUserFontSet();
294 if (aIndex < mRuleFaces.Length()) {
295 auto& entry = mRuleFaces[aIndex];
296 if (entry.mOrigin.value() != StyleOrigin::Author) {
297 return nullptr;
299 return entry.mFontFace;
302 aIndex -= mRuleFaces.Length();
303 if (aIndex < mNonRuleFaces.Length()) {
304 return mNonRuleFaces[aIndex].mFontFace;
307 return nullptr;
310 uint32_t FontFaceSet::Size() {
311 FlushUserFontSet();
313 // Web IDL objects can only expose array index properties up to INT32_MAX.
315 size_t total = mNonRuleFaces.Length();
316 for (const auto& entry : mRuleFaces) {
317 if (entry.mOrigin.value() == StyleOrigin::Author) {
318 ++total;
321 return std::min<size_t>(total, INT32_MAX);
324 uint32_t FontFaceSet::SizeIncludingNonAuthorOrigins() {
325 FlushUserFontSet();
327 // Web IDL objects can only expose array index properties up to INT32_MAX.
329 size_t total = mRuleFaces.Length() + mNonRuleFaces.Length();
330 return std::min<size_t>(total, INT32_MAX);
333 already_AddRefed<FontFaceSetIterator> FontFaceSet::Entries() {
334 RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, true);
335 return it.forget();
338 already_AddRefed<FontFaceSetIterator> FontFaceSet::Values() {
339 RefPtr<FontFaceSetIterator> it = new FontFaceSetIterator(this, false);
340 return it.forget();
343 void FontFaceSet::ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
344 JS::Handle<JS::Value> aThisArg, ErrorResult& aRv) {
345 JS::Rooted<JS::Value> thisArg(aCx, aThisArg);
346 for (size_t i = 0; i < SizeIncludingNonAuthorOrigins(); i++) {
347 RefPtr<FontFace> face = GetFontFaceAt(i);
348 if (!face) {
349 // The font at index |i| is a non-Author origin font, which we shouldn't
350 // expose per spec.
351 continue;
353 aCallback.Call(thisArg, *face, *face, *this, aRv);
354 if (aRv.Failed()) {
355 return;
360 bool FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules) {
361 // The impl object handles the callbacks for recreating the mRulesFaces array.
362 nsTArray<FontFaceRecord> oldRecords = std::move(mRuleFaces);
363 return mImpl->UpdateRules(aRules);
366 void FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, StyleOrigin aOrigin) {
367 MOZ_ASSERT(!HasRuleFontFace(aFontFace));
369 FontFaceRecord* rec = mRuleFaces.AppendElement();
370 rec->mFontFace = aFontFace;
371 rec->mOrigin = Some(aOrigin);
372 rec->mLoadEventShouldFire =
373 aFontFace->Status() == FontFaceLoadStatus::Unloaded ||
374 aFontFace->Status() == FontFaceLoadStatus::Loading;
377 void FontFaceSet::DidRefresh() { mImpl->CheckLoadingFinished(); }
379 void FontFaceSet::DispatchLoadingEventAndReplaceReadyPromise() {
380 gfxFontUtils::AssertSafeThreadOrServoFontMetricsLocked();
382 if (ServoStyleSet* set = gfxFontUtils::CurrentServoStyleSet()) {
383 // See comments in Gecko_GetFontMetrics.
385 // We can't just dispatch the runnable below if we're not on the main
386 // thread, since it needs to take a strong reference to the FontFaceSet,
387 // and being a DOM object, FontFaceSet doesn't support thread-safe
388 // refcounting. (Also, the Promise object creation must be done on
389 // the main thread.)
390 set->AppendTask(
391 PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this));
392 return;
395 (new AsyncEventDispatcher(this, u"loading"_ns, CanBubble::eNo))
396 ->PostDOMEvent();
398 if (mReady && mReady->State() != Promise::PromiseState::Pending) {
399 if (GetParentObject()) {
400 ErrorResult rv;
401 mReady = Promise::Create(GetParentObject(), rv);
405 // We may previously have been in a state where all fonts had finished
406 // loading and we'd set mResolveLazilyCreatedReadyPromise to make sure that
407 // if we lazily create mReady for a consumer that we resolve it before
408 // returning it. We're now loading fonts, so we need to clear that flag.
409 mResolveLazilyCreatedReadyPromise = false;
412 void FontFaceSet::MaybeResolve() {
413 if (mReady) {
414 mReady->MaybeResolve(this);
415 } else {
416 mResolveLazilyCreatedReadyPromise = true;
419 // Now dispatch the loadingdone/loadingerror events.
420 nsTArray<OwningNonNull<FontFace>> loaded;
421 nsTArray<OwningNonNull<FontFace>> failed;
423 auto checkStatus = [&](nsTArray<FontFaceRecord>& faces) -> void {
424 for (auto& face : faces) {
425 if (!face.mLoadEventShouldFire) {
426 continue;
428 FontFace* f = face.mFontFace;
429 switch (f->Status()) {
430 case FontFaceLoadStatus::Unloaded:
431 break;
432 case FontFaceLoadStatus::Loaded:
433 loaded.AppendElement(*f);
434 face.mLoadEventShouldFire = false;
435 break;
436 case FontFaceLoadStatus::Error:
437 failed.AppendElement(*f);
438 face.mLoadEventShouldFire = false;
439 break;
440 case FontFaceLoadStatus::Loading:
441 // We should've returned above at MightHavePendingFontLoads()!
442 MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus");
443 break;
448 checkStatus(mRuleFaces);
449 checkStatus(mNonRuleFaces);
451 DispatchLoadingFinishedEvent(u"loadingdone"_ns, std::move(loaded));
453 if (!failed.IsEmpty()) {
454 DispatchLoadingFinishedEvent(u"loadingerror"_ns, std::move(failed));
458 void FontFaceSet::DispatchLoadingFinishedEvent(
459 const nsAString& aType, nsTArray<OwningNonNull<FontFace>>&& aFontFaces) {
460 FontFaceSetLoadEventInit init;
461 init.mBubbles = false;
462 init.mCancelable = false;
463 init.mFontfaces = std::move(aFontFaces);
464 RefPtr<FontFaceSetLoadEvent> event =
465 FontFaceSetLoadEvent::Constructor(this, aType, init);
466 (new AsyncEventDispatcher(this, event.forget()))->PostDOMEvent();
469 void FontFaceSet::FlushUserFontSet() { mImpl->FlushUserFontSet(); }
471 void FontFaceSet::RefreshStandardFontLoadPrincipal() {
472 MOZ_ASSERT(NS_IsMainThread());
473 mImpl->RefreshStandardFontLoadPrincipal();
476 void FontFaceSet::CopyNonRuleFacesTo(FontFaceSet* aFontFaceSet) const {
477 for (const FontFaceRecord& rec : mNonRuleFaces) {
478 IgnoredErrorResult rv;
479 RefPtr<FontFace> f = rec.mFontFace;
480 aFontFaceSet->Add(*f, rv);
481 MOZ_ASSERT(!rv.Failed());
485 #undef LOG_ENABLED
486 #undef LOG