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 "FontFaceSetWorkerImpl.h"
8 #include "mozilla/FontLoaderUtils.h"
9 #include "mozilla/dom/WorkerPrivate.h"
10 #include "mozilla/dom/WorkerRef.h"
11 #include "mozilla/dom/WorkerRunnable.h"
12 #include "mozilla/LoadInfo.h"
13 #include "nsContentPolicyUtils.h"
14 #include "nsFontFaceLoader.h"
15 #include "nsINetworkPredictor.h"
16 #include "nsIWebNavigation.h"
18 using namespace mozilla
;
19 using namespace mozilla::css
;
21 namespace mozilla::dom
{
24 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, \
26 #define LOG_ENABLED() \
27 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
29 NS_IMPL_ISUPPORTS_INHERITED0(FontFaceSetWorkerImpl
, FontFaceSetImpl
);
31 FontFaceSetWorkerImpl::FontFaceSetWorkerImpl(FontFaceSet
* aOwner
)
32 : FontFaceSetImpl(aOwner
) {}
34 FontFaceSetWorkerImpl::~FontFaceSetWorkerImpl() = default;
36 bool FontFaceSetWorkerImpl::Initialize(WorkerPrivate
* aWorkerPrivate
) {
37 MOZ_ASSERT(aWorkerPrivate
);
39 RefPtr
<StrongWorkerRef
> workerRef
=
40 StrongWorkerRef::Create(aWorkerPrivate
, "FontFaceSetWorkerImpl",
41 [self
= RefPtr
{this}] { self
->Destroy(); });
42 if (NS_WARN_IF(!workerRef
)) {
47 RecursiveMutexAutoLock
lock(mMutex
);
48 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
51 class InitRunnable final
: public WorkerMainThreadRunnable
{
53 InitRunnable(WorkerPrivate
* aWorkerPrivate
, FontFaceSetWorkerImpl
* aImpl
)
54 : WorkerMainThreadRunnable(aWorkerPrivate
,
55 "FontFaceSetWorkerImpl :: Initialize"_ns
),
59 ~InitRunnable() override
= default;
61 bool MainThreadRun() override
{
62 mImpl
->InitializeOnMainThread();
66 FontFaceSetWorkerImpl
* mImpl
;
69 IgnoredErrorResult rv
;
70 auto runnable
= MakeRefPtr
<InitRunnable
>(aWorkerPrivate
, this);
71 runnable
->Dispatch(Canceling
, rv
);
72 return !NS_WARN_IF(rv
.Failed());
75 void FontFaceSetWorkerImpl::InitializeOnMainThread() {
76 MOZ_ASSERT(NS_IsMainThread());
77 RecursiveMutexAutoLock
lock(mMutex
);
83 WorkerPrivate
* workerPrivate
= mWorkerRef
->Private();
84 nsIPrincipal
* principal
= workerPrivate
->GetPrincipal();
85 nsIPrincipal
* loadingPrincipal
= workerPrivate
->GetLoadingPrincipal();
86 nsIPrincipal
* partitionedPrincipal
= workerPrivate
->GetPartitionedPrincipal();
87 nsIPrincipal
* defaultPrincipal
= principal
? principal
: loadingPrincipal
;
89 nsLoadFlags loadFlags
= workerPrivate
->GetLoadFlags();
90 uint32_t loadType
= 0;
92 // Get the top-level worker.
93 WorkerPrivate
* topWorkerPrivate
= workerPrivate
;
94 WorkerPrivate
* parent
= workerPrivate
->GetParent();
96 topWorkerPrivate
= parent
;
97 parent
= topWorkerPrivate
->GetParent();
100 // If the top-level worker is a dedicated worker and has a window, and the
101 // window has a docshell, the caching behavior of this worker should match
102 // that of that docshell. This matches the behaviour from
103 // WorkerScriptLoader::LoadScript.
104 if (topWorkerPrivate
->IsDedicatedWorker()) {
105 nsCOMPtr
<nsPIDOMWindowInner
> window
= topWorkerPrivate
->GetWindow();
107 nsCOMPtr
<nsIDocShell
> docShell
= window
->GetDocShell();
109 docShell
->GetDefaultLoadFlags(&loadFlags
);
110 docShell
->GetLoadType(&loadType
);
115 // Record the state of the "bypass cache" flags now. In theory the load type
116 // of a docshell could change after the document is loaded, but handling that
117 // doesn't seem too important. This matches the behaviour from
118 // FontFaceSetDocumentImpl::Initialize.
120 ((loadType
>> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE
) ||
121 (loadFlags
& nsIRequest::LOAD_BYPASS_CACHE
);
123 // Same for the "private browsing" flag.
124 if (defaultPrincipal
) {
125 mPrivateBrowsing
= defaultPrincipal
->GetPrivateBrowsingId() > 0;
128 mStandardFontLoadPrincipal
=
129 MakeRefPtr
<gfxFontSrcPrincipal
>(defaultPrincipal
, partitionedPrincipal
);
132 new URLExtraData(workerPrivate
->GetBaseURI(),
133 workerPrivate
->GetReferrerInfo(), defaultPrincipal
);
136 void FontFaceSetWorkerImpl::Destroy() {
137 RecursiveMutexAutoLock
lock(mMutex
);
139 mWorkerRef
= nullptr;
140 FontFaceSetImpl::Destroy();
143 bool FontFaceSetWorkerImpl::IsOnOwningThread() {
144 RecursiveMutexAutoLock
lock(mMutex
);
149 return mWorkerRef
->Private()->IsOnCurrentThread();
153 void FontFaceSetWorkerImpl::AssertIsOnOwningThread() {
154 RecursiveMutexAutoLock
lock(mMutex
);
156 MOZ_ASSERT(mWorkerRef
->Private()->IsOnCurrentThread());
158 // Asserting during cycle collection if we are tearing down the worker is
159 // difficult. The only other thread that uses FontFace(Set)Impl objects is
160 // the main thread (if created from a worker).
161 MOZ_ASSERT(!NS_IsMainThread());
166 void FontFaceSetWorkerImpl::DispatchToOwningThread(
167 const char* aName
, std::function
<void()>&& aFunc
) {
168 RecursiveMutexAutoLock
lock(mMutex
);
173 WorkerPrivate
* workerPrivate
= mWorkerRef
->Private();
174 if (workerPrivate
->IsOnCurrentThread()) {
175 NS_DispatchToCurrentThread(
176 NS_NewCancelableRunnableFunction(aName
, std::move(aFunc
)));
180 class FontFaceSetWorkerRunnable final
: public WorkerRunnable
{
182 FontFaceSetWorkerRunnable(WorkerPrivate
* aWorkerPrivate
,
183 std::function
<void()>&& aFunc
)
184 : WorkerRunnable(aWorkerPrivate
, "FontFaceSetWorkerRunnable"),
185 mFunc(std::move(aFunc
)) {}
187 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
193 std::function
<void()> mFunc
;
196 RefPtr
<FontFaceSetWorkerRunnable
> runnable
=
197 new FontFaceSetWorkerRunnable(workerPrivate
, std::move(aFunc
));
198 runnable
->Dispatch();
201 uint64_t FontFaceSetWorkerImpl::GetInnerWindowID() {
202 RecursiveMutexAutoLock
lock(mMutex
);
207 return mWorkerRef
->Private()->WindowID();
210 void FontFaceSetWorkerImpl::FlushUserFontSet() {
211 RecursiveMutexAutoLock
lock(mMutex
);
213 // If there was a change to the mNonRuleFaces array, then there could
214 // have been a modification to the user font set.
215 const bool modified
= mNonRuleFacesDirty
;
216 mNonRuleFacesDirty
= false;
218 for (size_t i
= 0, i_end
= mNonRuleFaces
.Length(); i
< i_end
; ++i
) {
219 InsertNonRuleFontFace(mNonRuleFaces
[i
].mFontFace
);
222 // Remove any residual families that have no font entries.
223 for (auto it
= mFontFamilies
.Iter(); !it
.Done(); it
.Next()) {
224 if (!it
.Data()->FontListLength()) {
230 IncrementGenerationLocked(true);
231 mHasLoadingFontFacesIsDirty
= true;
232 CheckLoadingStarted();
233 CheckLoadingFinished();
237 already_AddRefed
<gfxUserFontFamily
> FontFaceSetWorkerImpl::LookupFamily(
238 const nsACString
& aName
) const {
239 RecursiveMutexAutoLock
lock(mMutex
);
240 return gfxUserFontSet::LookupFamily(aName
);
243 nsresult
FontFaceSetWorkerImpl::StartLoad(gfxUserFontEntry
* aUserFontEntry
,
244 uint32_t aSrcIndex
) {
245 RecursiveMutexAutoLock
lock(mMutex
);
247 if (NS_WARN_IF(!mWorkerRef
)) {
248 return NS_ERROR_FAILURE
;
253 nsCOMPtr
<nsIStreamLoader
> streamLoader
;
255 const gfxFontFaceSrc
& src
= aUserFontEntry
->SourceAt(aSrcIndex
);
257 nsCOMPtr
<nsILoadGroup
> loadGroup(mWorkerRef
->Private()->GetLoadGroup());
258 nsCOMPtr
<nsIChannel
> channel
;
259 rv
= FontLoaderUtils::BuildChannel(
260 getter_AddRefs(channel
), src
.mURI
->get(), CORS_ANONYMOUS
,
261 dom::ReferrerPolicy::_empty
/* not used */, aUserFontEntry
, &src
,
262 mWorkerRef
->Private(), loadGroup
, nullptr);
263 NS_ENSURE_SUCCESS(rv
, rv
);
265 RefPtr
<nsFontFaceLoader
> fontLoader
=
266 new nsFontFaceLoader(aUserFontEntry
, aSrcIndex
, this, channel
);
269 nsCOMPtr
<nsIURI
> referrer
=
270 src
.mReferrerInfo
? src
.mReferrerInfo
->GetOriginalReferrer() : nullptr;
271 LOG("userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
272 fontLoader
.get(), src
.mURI
->GetSpecOrDefault().get(),
273 referrer
? referrer
->GetSpecOrDefault().get() : "");
276 rv
= NS_NewStreamLoader(getter_AddRefs(streamLoader
), fontLoader
, fontLoader
);
277 NS_ENSURE_SUCCESS(rv
, rv
);
279 rv
= channel
->AsyncOpen(streamLoader
);
281 fontLoader
->DropChannel(); // explicitly need to break ref cycle
284 mLoaders
.PutEntry(fontLoader
);
286 net::PredictorLearn(src
.mURI
->get(), mWorkerRef
->Private()->GetBaseURI(),
287 nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE
, loadGroup
);
289 if (NS_SUCCEEDED(rv
)) {
290 fontLoader
->StartedLoading(streamLoader
);
291 // let the font entry remember the loader, in case we need to cancel it
292 aUserFontEntry
->SetLoader(fontLoader
);
298 bool FontFaceSetWorkerImpl::IsFontLoadAllowed(const gfxFontFaceSrc
& aSrc
) {
299 MOZ_ASSERT(aSrc
.mSourceType
== gfxFontFaceSrc::eSourceType_URL
);
300 MOZ_ASSERT(NS_IsMainThread());
302 RecursiveMutexAutoLock
lock(mMutex
);
304 if (aSrc
.mUseOriginPrincipal
) {
308 if (NS_WARN_IF(!mWorkerRef
)) {
312 RefPtr
<gfxFontSrcPrincipal
> gfxPrincipal
=
313 aSrc
.mURI
->InheritsSecurityContext() ? nullptr
314 : aSrc
.LoadPrincipal(*this);
316 nsIPrincipal
* principal
=
317 gfxPrincipal
? gfxPrincipal
->NodePrincipal() : nullptr;
319 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
= new net::LoadInfo(
320 mWorkerRef
->Private()->GetLoadingPrincipal(), // loading principal
321 principal
, // triggering principal
322 nullptr, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
,
323 nsIContentPolicy::TYPE_FONT
);
325 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
327 NS_CheckContentLoadPolicy(aSrc
.mURI
->get(), secCheckLoadInfo
, &shouldLoad
,
328 nsContentUtils::GetContentPolicy());
330 return NS_SUCCEEDED(rv
) && NS_CP_ACCEPTED(shouldLoad
);
333 nsresult
FontFaceSetWorkerImpl::CreateChannelForSyncLoadFontData(
334 nsIChannel
** aOutChannel
, gfxUserFontEntry
* aFontToLoad
,
335 const gfxFontFaceSrc
* aFontFaceSrc
) {
336 RecursiveMutexAutoLock
lock(mMutex
);
337 if (NS_WARN_IF(!mWorkerRef
)) {
338 return NS_ERROR_FAILURE
;
341 gfxFontSrcPrincipal
* principal
= aFontToLoad
->GetPrincipal();
343 // We only get here for data: loads, so it doesn't really matter whether we
344 // use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be more
345 // restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
346 return NS_NewChannelWithTriggeringPrincipal(
347 aOutChannel
, aFontFaceSrc
->mURI
->get(),
348 mWorkerRef
->Private()->GetLoadingPrincipal(),
349 principal
? principal
->NodePrincipal() : nullptr,
350 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
,
351 aFontFaceSrc
->mUseOriginPrincipal
? nsIContentPolicy::TYPE_UA_FONT
352 : nsIContentPolicy::TYPE_FONT
);
355 nsPresContext
* FontFaceSetWorkerImpl::GetPresContext() const { return nullptr; }
357 TimeStamp
FontFaceSetWorkerImpl::GetNavigationStartTimeStamp() {
358 RecursiveMutexAutoLock
lock(mMutex
);
363 return mWorkerRef
->Private()->CreationTimeStamp();
366 already_AddRefed
<URLExtraData
> FontFaceSetWorkerImpl::GetURLExtraData() {
367 RecursiveMutexAutoLock
lock(mMutex
);
368 return RefPtr
{mURLExtraData
}.forget();
374 } // namespace mozilla::dom