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
,
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
,
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());
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
);
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
);
124 if (NS_WARN_IF(!impl
->Initialize(aWorkerPrivate
))) {
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
,
143 nsTArray
<RefPtr
<Promise
>> promises
;
145 nsTArray
<FontFace
*> faces
;
146 mImpl
->FindMatchingFontFaces(aFont
, aText
, faces
, aRv
);
151 for (FontFace
* f
: faces
) {
152 RefPtr
<Promise
> promise
= f
->Load(aRv
);
156 if (!promises
.AppendElement(promise
, fallible
)) {
157 aRv
.Throw(NS_ERROR_FAILURE
);
162 return Promise::All(aCx
, promises
, aRv
);
165 bool FontFaceSet::Check(const nsACString
& aFont
, const nsAString
& aText
,
169 nsTArray
<FontFace
*> faces
;
170 mImpl
->FindMatchingFontFaces(aFont
, aText
, faces
, aRv
);
175 for (FontFace
* f
: faces
) {
176 if (f
->Status() != FontFaceLoadStatus::Loaded
) {
184 bool FontFaceSet::ReadyPromiseIsPending() const {
185 return mReady
? mReady
->State() == Promise::PromiseState::Pending
186 : !mResolveLazilyCreatedReadyPromise
;
189 Promise
* FontFaceSet::GetReady(ErrorResult
& aRv
) {
190 mImpl
->EnsureReady();
193 nsCOMPtr
<nsIGlobalObject
> global
= GetParentObject();
194 mReady
= Promise::Create(global
, aRv
);
196 aRv
.Throw(NS_ERROR_FAILURE
);
199 if (mResolveLazilyCreatedReadyPromise
) {
200 mReady
->MaybeResolve(this);
201 mResolveLazilyCreatedReadyPromise
= false;
208 FontFaceSetLoadStatus
FontFaceSet::Status() { return mImpl
->Status(); }
211 bool FontFaceSet::HasRuleFontFace(FontFace
* aFontFace
) {
212 for (size_t i
= 0; i
< mRuleFaces
.Length(); i
++) {
213 if (mRuleFaces
[i
].mFontFace
== aFontFace
) {
221 void FontFaceSet::Add(FontFace
& aFontFace
, ErrorResult
& aRv
) {
224 FontFaceImpl
* fontImpl
= aFontFace
.GetImpl();
225 MOZ_ASSERT(fontImpl
);
227 if (!mImpl
->Add(fontImpl
, aRv
)) {
231 MOZ_ASSERT(!aRv
.Failed());
234 for (const FontFaceRecord
& rec
: mNonRuleFaces
) {
235 MOZ_ASSERT(rec
.mFontFace
!= &aFontFace
,
236 "FontFace should not occur in mNonRuleFaces twice");
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
);
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
);
272 if (!mImpl
->Delete(fontImpl
)) {
273 MOZ_ASSERT(!removed
, "Missing rule present in Impl!");
275 MOZ_ASSERT(removed
, "Rule present but missing in Impl!");
281 bool FontFaceSet::HasAvailableFontFace(FontFace
* aFontFace
) {
282 return aFontFace
->GetImpl()->IsInFontFaceSet(mImpl
);
285 bool FontFaceSet::Has(FontFace
& aFontFace
) {
288 return HasAvailableFontFace(&aFontFace
);
291 FontFace
* FontFaceSet::GetFontFaceAt(uint32_t aIndex
) {
294 if (aIndex
< mRuleFaces
.Length()) {
295 auto& entry
= mRuleFaces
[aIndex
];
296 if (entry
.mOrigin
.value() != StyleOrigin::Author
) {
299 return entry
.mFontFace
;
302 aIndex
-= mRuleFaces
.Length();
303 if (aIndex
< mNonRuleFaces
.Length()) {
304 return mNonRuleFaces
[aIndex
].mFontFace
;
310 uint32_t FontFaceSet::Size() {
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
) {
321 return std::min
<size_t>(total
, INT32_MAX
);
324 uint32_t FontFaceSet::SizeIncludingNonAuthorOrigins() {
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);
338 already_AddRefed
<FontFaceSetIterator
> FontFaceSet::Values() {
339 RefPtr
<FontFaceSetIterator
> it
= new FontFaceSetIterator(this, false);
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
);
349 // The font at index |i| is a non-Author origin font, which we shouldn't
353 aCallback
.Call(thisArg
, *face
, *face
, *this, aRv
);
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
391 PostTraversalTask::DispatchLoadingEventAndReplaceReadyPromise(this));
395 (new AsyncEventDispatcher(this, u
"loading"_ns
, CanBubble::eNo
))
398 if (mReady
&& mReady
->State() != Promise::PromiseState::Pending
) {
399 if (GetParentObject()) {
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() {
414 mReady
->MaybeResolve(this);
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
) {
428 FontFace
* f
= face
.mFontFace
;
429 switch (f
->Status()) {
430 case FontFaceLoadStatus::Unloaded
:
432 case FontFaceLoadStatus::Loaded
:
433 loaded
.AppendElement(*f
);
434 face
.mLoadEventShouldFire
= false;
436 case FontFaceLoadStatus::Error
:
437 failed
.AppendElement(*f
);
438 face
.mLoadEventShouldFire
= false;
440 case FontFaceLoadStatus::Loading
:
441 // We should've returned above at MightHavePendingFontLoads()!
442 case FontFaceLoadStatus::EndGuard_
:
443 MOZ_ASSERT_UNREACHABLE("unexpected FontFaceLoadStatus");
449 checkStatus(mRuleFaces
);
450 checkStatus(mNonRuleFaces
);
452 DispatchLoadingFinishedEvent(u
"loadingdone"_ns
, std::move(loaded
));
454 if (!failed
.IsEmpty()) {
455 DispatchLoadingFinishedEvent(u
"loadingerror"_ns
, std::move(failed
));
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();
470 void FontFaceSet::FlushUserFontSet() { mImpl
->FlushUserFontSet(); }
472 void FontFaceSet::RefreshStandardFontLoadPrincipal() {
473 MOZ_ASSERT(NS_IsMainThread());
474 mImpl
->RefreshStandardFontLoadPrincipal();
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());