summaryrefslogtreecommitdiffstats
path: root/layout/style/FontFaceSetWorkerImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/FontFaceSetWorkerImpl.cpp')
-rw-r--r--layout/style/FontFaceSetWorkerImpl.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/layout/style/FontFaceSetWorkerImpl.cpp b/layout/style/FontFaceSetWorkerImpl.cpp
new file mode 100644
index 0000000000..7cb945e2e8
--- /dev/null
+++ b/layout/style/FontFaceSetWorkerImpl.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FontFaceSetWorkerImpl.h"
+#include "FontPreloader.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
+#include "mozilla/dom/WorkerRunnable.h"
+#include "mozilla/LoadInfo.h"
+#include "nsContentPolicyUtils.h"
+#include "nsFontFaceLoader.h"
+#include "nsINetworkPredictor.h"
+#include "nsIWebNavigation.h"
+
+using namespace mozilla;
+using namespace mozilla::css;
+
+namespace mozilla::dom {
+
+#define LOG(...) \
+ MOZ_LOG(gfxUserFontSet::GetUserFontsLog(), mozilla::LogLevel::Debug, \
+ (__VA_ARGS__))
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gfxUserFontSet::GetUserFontsLog(), LogLevel::Debug)
+
+NS_IMPL_ISUPPORTS_INHERITED0(FontFaceSetWorkerImpl, FontFaceSetImpl);
+
+FontFaceSetWorkerImpl::FontFaceSetWorkerImpl(FontFaceSet* aOwner)
+ : FontFaceSetImpl(aOwner) {}
+
+FontFaceSetWorkerImpl::~FontFaceSetWorkerImpl() = default;
+
+bool FontFaceSetWorkerImpl::Initialize(WorkerPrivate* aWorkerPrivate) {
+ MOZ_ASSERT(aWorkerPrivate);
+
+ RefPtr<StrongWorkerRef> workerRef =
+ StrongWorkerRef::Create(aWorkerPrivate, "FontFaceSetWorkerImpl",
+ [self = RefPtr{this}] { self->Destroy(); });
+ if (NS_WARN_IF(!workerRef)) {
+ return false;
+ }
+
+ {
+ RecursiveMutexAutoLock lock(mMutex);
+ mWorkerRef = new ThreadSafeWorkerRef(workerRef);
+ }
+
+ class InitRunnable final : public WorkerMainThreadRunnable {
+ public:
+ InitRunnable(WorkerPrivate* aWorkerPrivate, FontFaceSetWorkerImpl* aImpl)
+ : WorkerMainThreadRunnable(aWorkerPrivate,
+ "FontFaceSetWorkerImpl :: Initialize"_ns),
+ mImpl(aImpl) {}
+
+ protected:
+ ~InitRunnable() override = default;
+
+ bool MainThreadRun() override {
+ mImpl->InitializeOnMainThread();
+ return true;
+ }
+
+ FontFaceSetWorkerImpl* mImpl;
+ };
+
+ IgnoredErrorResult rv;
+ auto runnable = MakeRefPtr<InitRunnable>(aWorkerPrivate, this);
+ runnable->Dispatch(Canceling, rv);
+ return !NS_WARN_IF(rv.Failed());
+}
+
+void FontFaceSetWorkerImpl::InitializeOnMainThread() {
+ MOZ_ASSERT(NS_IsMainThread());
+ RecursiveMutexAutoLock lock(mMutex);
+
+ if (!mWorkerRef) {
+ return;
+ }
+
+ WorkerPrivate* workerPrivate = mWorkerRef->Private();
+ nsIPrincipal* principal = workerPrivate->GetPrincipal();
+ nsIPrincipal* loadingPrincipal = workerPrivate->GetLoadingPrincipal();
+ nsIPrincipal* partitionedPrincipal = workerPrivate->GetPartitionedPrincipal();
+ nsIPrincipal* defaultPrincipal = principal ? principal : loadingPrincipal;
+
+ nsLoadFlags loadFlags = workerPrivate->GetLoadFlags();
+ uint32_t loadType = 0;
+
+ // Get the top-level worker.
+ WorkerPrivate* topWorkerPrivate = workerPrivate;
+ WorkerPrivate* parent = workerPrivate->GetParent();
+ while (parent) {
+ topWorkerPrivate = parent;
+ parent = topWorkerPrivate->GetParent();
+ }
+
+ // If the top-level worker is a dedicated worker and has a window, and the
+ // window has a docshell, the caching behavior of this worker should match
+ // that of that docshell. This matches the behaviour from
+ // WorkerScriptLoader::LoadScript.
+ if (topWorkerPrivate->IsDedicatedWorker()) {
+ nsCOMPtr<nsPIDOMWindowInner> window = topWorkerPrivate->GetWindow();
+ if (window) {
+ nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
+ if (docShell) {
+ docShell->GetDefaultLoadFlags(&loadFlags);
+ docShell->GetLoadType(&loadType);
+ }
+ }
+ }
+
+ // Record the state of the "bypass cache" flags now. In theory the load type
+ // of a docshell could change after the document is loaded, but handling that
+ // doesn't seem too important. This matches the behaviour from
+ // FontFaceSetDocumentImpl::Initialize.
+ mBypassCache =
+ ((loadType >> 16) & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE) ||
+ (loadFlags & nsIRequest::LOAD_BYPASS_CACHE);
+
+ // Same for the "private browsing" flag.
+ if (defaultPrincipal) {
+ mPrivateBrowsing = defaultPrincipal->GetPrivateBrowsingId() > 0;
+ }
+
+ mStandardFontLoadPrincipal =
+ MakeRefPtr<gfxFontSrcPrincipal>(defaultPrincipal, partitionedPrincipal);
+
+ mURLExtraData =
+ new URLExtraData(workerPrivate->GetBaseURI(),
+ workerPrivate->GetReferrerInfo(), defaultPrincipal);
+}
+
+void FontFaceSetWorkerImpl::Destroy() {
+ RecursiveMutexAutoLock lock(mMutex);
+
+ mWorkerRef = nullptr;
+ FontFaceSetImpl::Destroy();
+}
+
+bool FontFaceSetWorkerImpl::IsOnOwningThread() {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (!mWorkerRef) {
+ return false;
+ }
+
+ return mWorkerRef->Private()->IsOnCurrentThread();
+}
+
+#ifdef DEBUG
+void FontFaceSetWorkerImpl::AssertIsOnOwningThread() {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (mWorkerRef) {
+ MOZ_ASSERT(mWorkerRef->Private()->IsOnCurrentThread());
+ } else {
+ // Asserting during cycle collection if we are tearing down the worker is
+ // difficult. The only other thread that uses FontFace(Set)Impl objects is
+ // the main thread (if created from a worker).
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+}
+#endif
+
+void FontFaceSetWorkerImpl::DispatchToOwningThread(
+ const char* aName, std::function<void()>&& aFunc) {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (!mWorkerRef) {
+ return;
+ }
+
+ WorkerPrivate* workerPrivate = mWorkerRef->Private();
+ if (workerPrivate->IsOnCurrentThread()) {
+ NS_DispatchToCurrentThread(
+ NS_NewCancelableRunnableFunction(aName, std::move(aFunc)));
+ return;
+ }
+
+ class FontFaceSetWorkerRunnable final : public WorkerRunnable {
+ public:
+ FontFaceSetWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+ std::function<void()>&& aFunc)
+ : WorkerRunnable(aWorkerPrivate), mFunc(std::move(aFunc)) {}
+
+ bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
+ mFunc();
+ return true;
+ }
+
+ private:
+ std::function<void()> mFunc;
+ };
+
+ RefPtr<FontFaceSetWorkerRunnable> runnable =
+ new FontFaceSetWorkerRunnable(workerPrivate, std::move(aFunc));
+ runnable->Dispatch();
+}
+
+uint64_t FontFaceSetWorkerImpl::GetInnerWindowID() {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (!mWorkerRef) {
+ return 0;
+ }
+
+ return mWorkerRef->Private()->WindowID();
+}
+
+void FontFaceSetWorkerImpl::FlushUserFontSet() {
+ RecursiveMutexAutoLock lock(mMutex);
+
+ // If there was a change to the mNonRuleFaces array, then there could
+ // have been a modification to the user font set.
+ bool modified = mNonRuleFacesDirty;
+ mNonRuleFacesDirty = false;
+
+ for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
+ InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
+ }
+
+ // Remove any residual families that have no font entries.
+ for (auto it = mFontFamilies.Iter(); !it.Done(); it.Next()) {
+ if (!it.Data()->FontListLength()) {
+ it.Remove();
+ }
+ }
+
+ if (modified) {
+ IncrementGeneration(true);
+ mHasLoadingFontFacesIsDirty = true;
+ CheckLoadingStarted();
+ CheckLoadingFinished();
+ }
+}
+
+already_AddRefed<gfxUserFontFamily> FontFaceSetWorkerImpl::LookupFamily(
+ const nsACString& aName) const {
+ RecursiveMutexAutoLock lock(mMutex);
+ return gfxUserFontSet::LookupFamily(aName);
+}
+
+nsresult FontFaceSetWorkerImpl::StartLoad(gfxUserFontEntry* aUserFontEntry,
+ uint32_t aSrcIndex) {
+ RecursiveMutexAutoLock lock(mMutex);
+
+ if (NS_WARN_IF(!mWorkerRef)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ nsCOMPtr<nsIStreamLoader> streamLoader;
+
+ const gfxFontFaceSrc& src = aUserFontEntry->SourceAt(aSrcIndex);
+
+ nsCOMPtr<nsILoadGroup> loadGroup(mWorkerRef->Private()->GetLoadGroup());
+ nsCOMPtr<nsIChannel> channel;
+ rv = FontPreloader::BuildChannel(
+ getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
+ dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
+ mWorkerRef->Private(), loadGroup, nullptr, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsFontFaceLoader> fontLoader =
+ new nsFontFaceLoader(aUserFontEntry, aSrcIndex, this, channel);
+
+ if (LOG_ENABLED()) {
+ nsCOMPtr<nsIURI> referrer =
+ src.mReferrerInfo ? src.mReferrerInfo->GetOriginalReferrer() : nullptr;
+ LOG("userfonts (%p) download start - font uri: (%s) referrer uri: (%s)\n",
+ fontLoader.get(), src.mURI->GetSpecOrDefault().get(),
+ referrer ? referrer->GetSpecOrDefault().get() : "");
+ }
+
+ rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader, fontLoader);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = channel->AsyncOpen(streamLoader);
+ if (NS_FAILED(rv)) {
+ fontLoader->DropChannel(); // explicitly need to break ref cycle
+ }
+
+ mLoaders.PutEntry(fontLoader);
+
+ net::PredictorLearn(src.mURI->get(), mWorkerRef->Private()->GetBaseURI(),
+ nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadGroup);
+
+ if (NS_SUCCEEDED(rv)) {
+ fontLoader->StartedLoading(streamLoader);
+ // let the font entry remember the loader, in case we need to cancel it
+ aUserFontEntry->SetLoader(fontLoader);
+ }
+
+ return rv;
+}
+
+bool FontFaceSetWorkerImpl::IsFontLoadAllowed(const gfxFontFaceSrc& aSrc) {
+ MOZ_ASSERT(aSrc.mSourceType == gfxFontFaceSrc::eSourceType_URL);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RecursiveMutexAutoLock lock(mMutex);
+
+ if (aSrc.mUseOriginPrincipal) {
+ return true;
+ }
+
+ if (NS_WARN_IF(!mWorkerRef)) {
+ return false;
+ }
+
+ RefPtr<gfxFontSrcPrincipal> gfxPrincipal =
+ aSrc.mURI->InheritsSecurityContext() ? nullptr
+ : aSrc.LoadPrincipal(*this);
+
+ nsIPrincipal* principal =
+ gfxPrincipal ? gfxPrincipal->NodePrincipal() : nullptr;
+
+ nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
+ mWorkerRef->Private()->GetLoadingPrincipal(), // loading principal
+ principal, // triggering principal
+ nullptr, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
+ nsIContentPolicy::TYPE_FONT);
+
+ int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+ nsresult rv = NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo,
+ ""_ns, // mime type
+ &shouldLoad,
+ nsContentUtils::GetContentPolicy());
+
+ return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
+}
+
+nsresult FontFaceSetWorkerImpl::CreateChannelForSyncLoadFontData(
+ nsIChannel** aOutChannel, gfxUserFontEntry* aFontToLoad,
+ const gfxFontFaceSrc* aFontFaceSrc) {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (NS_WARN_IF(!mWorkerRef)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ gfxFontSrcPrincipal* principal = aFontToLoad->GetPrincipal();
+
+ // We only get here for data: loads, so it doesn't really matter whether we
+ // use SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT or not, to be more
+ // restrictive we use SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT.
+ return NS_NewChannelWithTriggeringPrincipal(
+ aOutChannel, aFontFaceSrc->mURI->get(),
+ mWorkerRef->Private()->GetLoadingPrincipal(),
+ principal ? principal->NodePrincipal() : nullptr,
+ nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
+ aFontFaceSrc->mUseOriginPrincipal ? nsIContentPolicy::TYPE_UA_FONT
+ : nsIContentPolicy::TYPE_FONT);
+}
+
+nsPresContext* FontFaceSetWorkerImpl::GetPresContext() const { return nullptr; }
+
+TimeStamp FontFaceSetWorkerImpl::GetNavigationStartTimeStamp() {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (!mWorkerRef) {
+ return TimeStamp();
+ }
+
+ return mWorkerRef->Private()->CreationTimeStamp();
+}
+
+already_AddRefed<URLExtraData> FontFaceSetWorkerImpl::GetURLExtraData() {
+ RecursiveMutexAutoLock lock(mMutex);
+ return RefPtr{mURLExtraData}.forget();
+}
+
+#undef LOG_ENABLED
+#undef LOG
+
+} // namespace mozilla::dom