FontFaceSetWorkerImpl.cpp (12080B)
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 "FontFaceSetWorkerImpl.h" 8 9 #include "mozilla/FontLoaderUtils.h" 10 #include "mozilla/LoadInfo.h" 11 #include "mozilla/dom/WorkerPrivate.h" 12 #include "mozilla/dom/WorkerRef.h" 13 #include "mozilla/dom/WorkerRunnable.h" 14 #include "nsContentPolicyUtils.h" 15 #include "nsFontFaceLoader.h" 16 #include "nsIWebNavigation.h" 17 18 using namespace mozilla; 19 using namespace mozilla::css; 20 21 namespace mozilla::dom { 22 23 #define LOG(...) \ 24 MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, \ 25 (__VA_ARGS__)) 26 #define LOG_ENABLED() \ 27 MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug) 28 29 NS_IMPL_ISUPPORTS_INHERITED0(FontFaceSetWorkerImpl, FontFaceSetImpl); 30 31 FontFaceSetWorkerImpl::FontFaceSetWorkerImpl(FontFaceSet* aOwner) 32 : FontFaceSetImpl(aOwner) {} 33 34 FontFaceSetWorkerImpl::~FontFaceSetWorkerImpl() = default; 35 36 bool FontFaceSetWorkerImpl::Initialize(WorkerPrivate* aWorkerPrivate) { 37 MOZ_ASSERT(aWorkerPrivate); 38 39 RefPtr<StrongWorkerRef> workerRef = 40 StrongWorkerRef::Create(aWorkerPrivate, "FontFaceSetWorkerImpl", 41 [self = RefPtr{this}] { self->Destroy(); }); 42 if (NS_WARN_IF(!workerRef)) { 43 return false; 44 } 45 46 { 47 RecursiveMutexAutoLock lock(mMutex); 48 mWorkerRef = new ThreadSafeWorkerRef(workerRef); 49 } 50 51 class InitRunnable final : public WorkerMainThreadRunnable { 52 public: 53 InitRunnable(WorkerPrivate* aWorkerPrivate, FontFaceSetWorkerImpl* aImpl) 54 : WorkerMainThreadRunnable(aWorkerPrivate, 55 "FontFaceSetWorkerImpl :: Initialize"_ns), 56 mImpl(aImpl) {} 57 58 protected: 59 ~InitRunnable() override = default; 60 61 bool MainThreadRun() override { 62 mImpl->InitializeOnMainThread(); 63 return true; 64 } 65 66 FontFaceSetWorkerImpl* mImpl; 67 }; 68 69 IgnoredErrorResult rv; 70 auto runnable = MakeRefPtr<InitRunnable>(aWorkerPrivate, this); 71 runnable->Dispatch(aWorkerPrivate, Canceling, rv); 72 return !NS_WARN_IF(rv.Failed()); 73 } 74 75 void FontFaceSetWorkerImpl::InitializeOnMainThread() { 76 MOZ_ASSERT(NS_IsMainThread()); 77 RecursiveMutexAutoLock lock(mMutex); 78 79 if (!mWorkerRef) { 80 return; 81 } 82 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; 88 89 nsLoadFlags loadFlags = workerPrivate->GetLoadFlags(); 90 uint32_t loadType = 0; 91 92 // Get the top-level worker. 93 WorkerPrivate* topWorkerPrivate = workerPrivate; 94 WorkerPrivate* parent = workerPrivate->GetParent(); 95 while (parent) { 96 topWorkerPrivate = parent; 97 parent = topWorkerPrivate->GetParent(); 98 } 99 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(); 106 if (window) { 107 nsCOMPtr<nsIDocShell> docShell = window->GetDocShell(); 108 if (docShell) { 109 docShell->GetDefaultLoadFlags(&loadFlags); 110 docShell->GetLoadType(&loadType); 111 } 112 } 113 } 114 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. 119 mBypassCache = 120 ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) || 121 (loadFlags & nsIRequest::LOAD_BYPASS_CACHE); 122 123 // Same for the "private browsing" flag. 124 if (defaultPrincipal) { 125 mPrivateBrowsing = defaultPrincipal->GetIsInPrivateBrowsing(); 126 } 127 128 mStandardFontLoadPrincipal = 129 MakeRefPtr<gfxFontSrcPrincipal>(defaultPrincipal, partitionedPrincipal); 130 131 mURLExtraData = 132 new URLExtraData(workerPrivate->GetBaseURI(), 133 workerPrivate->GetReferrerInfo(), defaultPrincipal); 134 } 135 136 void FontFaceSetWorkerImpl::Destroy() { 137 RecursiveMutexAutoLock lock(mMutex); 138 139 mWorkerRef = nullptr; 140 FontFaceSetImpl::Destroy(); 141 } 142 143 bool FontFaceSetWorkerImpl::IsOnOwningThread() { 144 RecursiveMutexAutoLock lock(mMutex); 145 if (!mWorkerRef) { 146 return false; 147 } 148 149 return mWorkerRef->Private()->IsOnCurrentThread(); 150 } 151 152 #ifdef DEBUG 153 void FontFaceSetWorkerImpl::AssertIsOnOwningThread() { 154 RecursiveMutexAutoLock lock(mMutex); 155 if (mWorkerRef) { 156 MOZ_ASSERT(mWorkerRef->Private()->IsOnCurrentThread()); 157 } else { 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()); 162 } 163 } 164 #endif 165 166 void FontFaceSetWorkerImpl::DispatchToOwningThread( 167 const char* aName, std::function<void()>&& aFunc) { 168 RecursiveMutexAutoLock lock(mMutex); 169 if (!mWorkerRef) { 170 return; 171 } 172 173 WorkerPrivate* workerPrivate = mWorkerRef->Private(); 174 if (workerPrivate->IsOnCurrentThread()) { 175 NS_DispatchToCurrentThread( 176 NS_NewCancelableRunnableFunction(aName, std::move(aFunc))); 177 return; 178 } 179 180 class FontFaceSetWorkerRunnable final : public WorkerThreadRunnable { 181 public: 182 FontFaceSetWorkerRunnable(WorkerPrivate* aWorkerPrivate, 183 std::function<void()>&& aFunc) 184 : WorkerThreadRunnable("FontFaceSetWorkerRunnable"), 185 mFunc(std::move(aFunc)) {} 186 187 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 188 mFunc(); 189 return true; 190 } 191 192 private: 193 std::function<void()> mFunc; 194 }; 195 196 auto runnable = 197 MakeRefPtr<FontFaceSetWorkerRunnable>(workerPrivate, std::move(aFunc)); 198 runnable->Dispatch(workerPrivate); 199 } 200 201 uint64_t FontFaceSetWorkerImpl::GetInnerWindowID() { 202 RecursiveMutexAutoLock lock(mMutex); 203 if (!mWorkerRef) { 204 return 0; 205 } 206 207 return mWorkerRef->Private()->WindowID(); 208 } 209 210 void FontFaceSetWorkerImpl::FlushUserFontSet() { 211 RecursiveMutexAutoLock lock(mMutex); 212 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; 217 218 for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) { 219 InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace); 220 } 221 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()) { 225 it.Remove(); 226 } 227 } 228 229 if (modified) { 230 IncrementGenerationLocked(true); 231 mHasLoadingFontFacesIsDirty = true; 232 CheckLoadingStarted(); 233 CheckLoadingFinished(); 234 } 235 } 236 237 already_AddRefed<gfxUserFontFamily> FontFaceSetWorkerImpl::LookupFamily( 238 const nsACString& aName) const { 239 RecursiveMutexAutoLock lock(mMutex); 240 return gfxUserFontSet::LookupFamily(aName); 241 } 242 243 nsresult FontFaceSetWorkerImpl::StartLoad(gfxUserFontEntry* aUserFontEntry, 244 uint32_t aSrcIndex) { 245 RecursiveMutexAutoLock lock(mMutex); 246 247 if (NS_WARN_IF(!mWorkerRef)) { 248 return NS_ERROR_FAILURE; 249 } 250 251 nsresult rv; 252 253 nsCOMPtr<nsIStreamLoader> streamLoader; 254 255 const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex); 256 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); 264 265 auto fontLoader = 266 MakeRefPtr<nsFontFaceLoader>(aUserFontEntry, aSrcIndex, this, channel); 267 268 if (LOG_ENABLED()) { 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() : ""); 274 } 275 276 rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, fontLoader); 277 NS_ENSURE_SUCCESS(rv, rv); 278 279 rv = channel->AsyncOpen(streamLoader); 280 if (NS_FAILED(rv)) { 281 fontLoader->DropChannel(); // explicitly need to break ref cycle 282 } 283 284 mLoaders.PutEntry(fontLoader); 285 286 if (NS_SUCCEEDED(rv)) { 287 fontLoader->StartedLoading(streamLoader); 288 // let the font entry remember the loader, in case we need to cancel it 289 aUserFontEntry->SetLoader(fontLoader); 290 } 291 292 return rv; 293 } 294 295 bool FontFaceSetWorkerImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) { 296 MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL); 297 MOZ_ASSERT(NS_IsMainThread()); 298 299 RecursiveMutexAutoLock lock(mMutex); 300 301 if (aSrc.mUseOriginPrincipal) { 302 return true; 303 } 304 305 if (NS_WARN_IF(!mWorkerRef)) { 306 return false; 307 } 308 309 RefPtr<gfxFontSrcPrincipal> gfxPrincipal = 310 aSrc.mURI->InheritsSecurityContext() ? nullptr 311 : aSrc.LoadPrincipal(*this); 312 313 nsIPrincipal* principal = 314 gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr; 315 316 Result<RefPtr<net::LoadInfo>, nsresult> maybeLoadInfo = net::LoadInfo::Create( 317 mWorkerRef->Private()->GetLoadingPrincipal(), // loading principal 318 principal, // triggering principal 319 nullptr, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 320 nsIContentPolicy::TYPE_FONT); 321 if (NS_WARN_IF(maybeLoadInfo.isErr())) { 322 return false; 323 } 324 RefPtr<net::LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap(); 325 326 int16_t shouldLoad = nsIContentPolicy::ACCEPT; 327 nsresult rv = 328 NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &shouldLoad, 329 nsContentUtils::GetContentPolicy()); 330 331 return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad); 332 } 333 334 nsresult FontFaceSetWorkerImpl::CreateChannelForSyncLoadFontData( 335 nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad, 336 const gfxFontFaceSrc* aFontFaceSrc) { 337 RecursiveMutexAutoLock lock(mMutex); 338 if (NS_WARN_IF(!mWorkerRef)) { 339 return NS_ERROR_FAILURE; 340 } 341 342 gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal(); 343 344 // We only get here for data: loads, so it doesn't really matter whether we 345 // use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be more 346 // restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT. 347 return NS_NewChannelWithTriggeringPrincipal( 348 aOutChannel, aFontFaceSrc->mURI->get(), 349 mWorkerRef->Private()->GetLoadingPrincipal(), 350 principal ? principal->NodePrincipal() : nullptr, 351 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT, 352 aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT 353 : nsIContentPolicy::TYPE_FONT); 354 } 355 356 FontVisibilityProvider* FontFaceSetWorkerImpl::GetFontVisibilityProvider() 357 const { 358 RecursiveMutexAutoLock lock(mMutex); 359 return mWorkerRef->Private(); 360 } 361 362 TimeStamp FontFaceSetWorkerImpl::GetNavigationStartTimeStamp() { 363 RecursiveMutexAutoLock lock(mMutex); 364 if (!mWorkerRef) { 365 return TimeStamp(); 366 } 367 368 return mWorkerRef->Private()->CreationTimeStamp(); 369 } 370 371 already_AddRefed<URLExtraData> FontFaceSetWorkerImpl::GetURLExtraData() { 372 RecursiveMutexAutoLock lock(mMutex); 373 return RefPtr{mURLExtraData}.forget(); 374 } 375 376 #undef LOG_ENABLED 377 #undef LOG 378 379 } // namespace mozilla::dom