1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
|
/* -*- 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 "mozilla/FontLoaderUtils.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(aWorkerPrivate, 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->GetIsInPrivateBrowsing();
}
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 WorkerThreadRunnable {
public:
FontFaceSetWorkerRunnable(WorkerPrivate* aWorkerPrivate,
std::function<void()>&& aFunc)
: WorkerThreadRunnable("FontFaceSetWorkerRunnable"),
mFunc(std::move(aFunc)) {}
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
mFunc();
return true;
}
private:
std::function<void()> mFunc;
};
auto runnable =
MakeRefPtr<FontFaceSetWorkerRunnable>(workerPrivate, std::move(aFunc));
runnable->Dispatch(workerPrivate);
}
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.
const bool modified = mNonRuleFacesDirty;
mNonRuleFacesDirty = false;
for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace);
}
// 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) {
IncrementGenerationLocked(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 = FontLoaderUtils::BuildChannel(
getter_AddRefs(channel), src.mURI->get(), CORS_ANONYMOUS,
dom::ReferrerPolicy::_empty /* not used */, aUserFontEntry, &src,
mWorkerRef->Private(), loadGroup, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
auto fontLoader =
MakeRefPtr<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;
Result<RefPtr<net::LoadInfo>, nsresult> maybeLoadInfo = net::LoadInfo::Create(
mWorkerRef->Private()->GetLoadingPrincipal(), // loading principal
principal, // triggering principal
nullptr, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
nsIContentPolicy::TYPE_FONT);
if (NS_WARN_IF(maybeLoadInfo.isErr())) {
return false;
}
RefPtr<net::LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap();
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
nsresult rv =
NS_CheckContentLoadPolicy(aSrc.mURI->get(), secCheckLoadInfo, &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
|