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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "WorkerLoadInfo.h"
8 #include "WorkerPrivate.h"
10 #include "mozilla/BasePrincipal.h"
11 #include "mozilla/dom/nsCSPUtils.h"
12 #include "mozilla/dom/BrowserChild.h"
13 #include "mozilla/dom/ReferrerInfo.h"
14 #include "mozilla/ipc/BackgroundUtils.h"
15 #include "mozilla/ipc/PBackgroundSharedTypes.h"
16 #include "mozilla/LoadContext.h"
17 #include "mozilla/StorageAccess.h"
18 #include "mozilla/StoragePrincipalHelper.h"
19 #include "nsContentUtils.h"
20 #include "nsIContentSecurityPolicy.h"
21 #include "nsICookieJarSettings.h"
22 #include "nsINetworkInterceptController.h"
23 #include "nsIProtocolHandler.h"
24 #include "nsIReferrerInfo.h"
25 #include "nsIBrowserChild.h"
26 #include "nsScriptSecurityManager.h"
27 #include "nsNetUtil.h"
37 class MainThreadReleaseRunnable final
: public Runnable
{
38 nsTArray
<nsCOMPtr
<nsISupports
>> mDoomed
;
39 nsCOMPtr
<nsILoadGroup
> mLoadGroupToCancel
;
42 MainThreadReleaseRunnable(nsTArray
<nsCOMPtr
<nsISupports
>>&& aDoomed
,
43 nsCOMPtr
<nsILoadGroup
>&& aLoadGroupToCancel
)
44 : mozilla::Runnable("MainThreadReleaseRunnable"),
45 mDoomed(std::move(aDoomed
)),
46 mLoadGroupToCancel(std::move(aLoadGroupToCancel
)) {}
48 NS_INLINE_DECL_REFCOUNTING_INHERITED(MainThreadReleaseRunnable
, Runnable
)
52 if (mLoadGroupToCancel
) {
53 mLoadGroupToCancel
->CancelWithReason(
54 NS_BINDING_ABORTED
, "WorkerLoadInfo::MainThreadReleaseRunnable"_ns
);
55 mLoadGroupToCancel
= nullptr;
63 ~MainThreadReleaseRunnable() = default;
66 // Specialize this if there's some class that has multiple nsISupports bases.
68 struct ISupportsBaseInfo
{
69 using ISupportsBase
= T
;
72 template <template <class> class SmartPtr
, class T
>
73 inline void SwapToISupportsArray(SmartPtr
<T
>& aSrc
,
74 nsTArray
<nsCOMPtr
<nsISupports
>>& aDest
) {
75 nsCOMPtr
<nsISupports
>* dest
= aDest
.AppendElement();
80 nsISupports
* rawSupports
=
81 static_cast<typename ISupportsBaseInfo
<T
>::ISupportsBase
*>(raw
);
82 dest
->swap(rawSupports
);
87 WorkerLoadInfoData::WorkerLoadInfoData()
88 : mLoadFlags(nsIRequest::LOAD_NORMAL
),
89 mWindowID(UINT64_MAX
),
90 mAssociatedBrowsingContextID(0),
91 mReferrerInfo(new ReferrerInfo(nullptr)),
94 mReportEvalCSPViolations(false),
95 mWasmEvalAllowed(false),
96 mReportWasmEvalCSPViolations(false),
97 mXHRParamsAllowed(false),
98 mWatchedByDevTools(false),
99 mStorageAccess(StorageAccess::eDeny
),
100 mUseRegularPrincipal(false),
101 mUsingStorageAccess(false),
102 mServiceWorkersTestingInWindow(false),
103 mShouldResistFingerprinting(false),
104 mIsThirdPartyContext(true),
105 mSecureContext(eNotSet
) {}
107 nsresult
WorkerLoadInfo::SetPrincipalsAndCSPOnMainThread(
108 nsIPrincipal
* aPrincipal
, nsIPrincipal
* aPartitionedPrincipal
,
109 nsILoadGroup
* aLoadGroup
, nsIContentSecurityPolicy
* aCsp
) {
110 AssertIsOnMainThread();
111 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup
, aPrincipal
));
113 mPrincipal
= aPrincipal
;
114 mPartitionedPrincipal
= aPartitionedPrincipal
;
119 mCSP
->GetAllowsEval(&mReportEvalCSPViolations
, &mEvalAllowed
);
120 mCSP
->GetAllowsWasmEval(&mReportWasmEvalCSPViolations
, &mWasmEvalAllowed
);
121 mCSPInfo
= MakeUnique
<CSPInfo
>();
122 nsresult rv
= CSPToCSPInfo(aCsp
, mCSPInfo
.get());
123 if (NS_WARN_IF(NS_FAILED(rv
))) {
128 mReportEvalCSPViolations
= false;
129 mWasmEvalAllowed
= true;
130 mReportWasmEvalCSPViolations
= false;
133 mLoadGroup
= aLoadGroup
;
135 mPrincipalInfo
= MakeUnique
<PrincipalInfo
>();
136 mPartitionedPrincipalInfo
= MakeUnique
<PrincipalInfo
>();
137 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(
138 aLoadGroup
, mOriginAttributes
);
140 nsresult rv
= PrincipalToPrincipalInfo(aPrincipal
, mPrincipalInfo
.get());
141 NS_ENSURE_SUCCESS(rv
, rv
);
143 if (aPrincipal
->Equals(aPartitionedPrincipal
)) {
144 *mPartitionedPrincipalInfo
= *mPrincipalInfo
;
146 mPartitionedPrincipalInfo
= MakeUnique
<PrincipalInfo
>();
147 rv
= PrincipalToPrincipalInfo(aPartitionedPrincipal
,
148 mPartitionedPrincipalInfo
.get());
149 NS_ENSURE_SUCCESS(rv
, rv
);
154 nsresult
WorkerLoadInfo::GetPrincipalsAndLoadGroupFromChannel(
155 nsIChannel
* aChannel
, nsIPrincipal
** aPrincipalOut
,
156 nsIPrincipal
** aPartitionedPrincipalOut
, nsILoadGroup
** aLoadGroupOut
) {
157 AssertIsOnMainThread();
158 MOZ_DIAGNOSTIC_ASSERT(aChannel
);
159 MOZ_DIAGNOSTIC_ASSERT(aPrincipalOut
);
160 MOZ_DIAGNOSTIC_ASSERT(aPartitionedPrincipalOut
);
161 MOZ_DIAGNOSTIC_ASSERT(aLoadGroupOut
);
163 // Initial triggering principal should be set
164 NS_ENSURE_TRUE(mLoadingPrincipal
, NS_ERROR_DOM_INVALID_STATE_ERR
);
166 nsIScriptSecurityManager
* ssm
= nsContentUtils::GetSecurityManager();
167 MOZ_DIAGNOSTIC_ASSERT(ssm
);
169 nsCOMPtr
<nsIPrincipal
> channelPrincipal
;
170 nsCOMPtr
<nsIPrincipal
> channelPartitionedPrincipal
;
171 nsresult rv
= ssm
->GetChannelResultPrincipals(
172 aChannel
, getter_AddRefs(channelPrincipal
),
173 getter_AddRefs(channelPartitionedPrincipal
));
174 NS_ENSURE_SUCCESS(rv
, rv
);
176 // Every time we call GetChannelResultPrincipal() it will return a different
177 // null principal for a data URL. We don't want to change the worker's
178 // principal again, though. Instead just keep the original null principal we
179 // first got from the channel.
181 // Note, we don't do this by setting principalToInherit on the channel's
182 // load info because we don't yet have the first null principal when we
183 // create the channel.
184 if (mPrincipal
&& mPrincipal
->GetIsNullPrincipal() &&
185 channelPrincipal
->GetIsNullPrincipal()) {
186 channelPrincipal
= mPrincipal
;
187 channelPartitionedPrincipal
= mPrincipal
;
190 nsCOMPtr
<nsILoadGroup
> channelLoadGroup
;
191 rv
= aChannel
->GetLoadGroup(getter_AddRefs(channelLoadGroup
));
192 NS_ENSURE_SUCCESS(rv
, rv
);
193 MOZ_ASSERT(channelLoadGroup
);
195 // If the loading principal is the system principal then the channel
196 // principal must also be the system principal (we do not allow chrome
197 // code to create workers with non-chrome scripts, and if we ever decide
198 // to change this we need to make sure we don't always set
199 // mPrincipalIsSystem to true in WorkerPrivate::GetLoadInfo()). Otherwise
200 // this channel principal must be same origin with the load principal (we
201 // check again here in case redirects changed the location of the script).
202 if (mLoadingPrincipal
->IsSystemPrincipal()) {
203 if (!channelPrincipal
->IsSystemPrincipal()) {
204 nsCOMPtr
<nsIURI
> finalURI
;
205 rv
= NS_GetFinalChannelURI(aChannel
, getter_AddRefs(finalURI
));
206 NS_ENSURE_SUCCESS(rv
, rv
);
208 // See if this is a resource URI. Since JSMs usually come from
209 // resource:// URIs we're currently considering all URIs with the
210 // URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
212 rv
= NS_URIChainHasFlags(finalURI
, nsIProtocolHandler::URI_IS_UI_RESOURCE
,
214 NS_ENSURE_SUCCESS(rv
, rv
);
217 // Assign the system principal to the resource:// worker only if it
218 // was loaded from code using the system principal.
219 channelPrincipal
= mLoadingPrincipal
;
220 channelPartitionedPrincipal
= mLoadingPrincipal
;
222 return NS_ERROR_DOM_BAD_URI
;
227 // The principal can change, but it should still match the original
228 // load group's browser element flag.
229 MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(channelLoadGroup
, channelPrincipal
));
231 channelPrincipal
.forget(aPrincipalOut
);
232 channelPartitionedPrincipal
.forget(aPartitionedPrincipalOut
);
233 channelLoadGroup
.forget(aLoadGroupOut
);
238 nsresult
WorkerLoadInfo::SetPrincipalsAndCSPFromChannel(nsIChannel
* aChannel
) {
239 AssertIsOnMainThread();
241 nsCOMPtr
<nsIPrincipal
> principal
;
242 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
243 nsCOMPtr
<nsILoadGroup
> loadGroup
;
244 nsresult rv
= GetPrincipalsAndLoadGroupFromChannel(
245 aChannel
, getter_AddRefs(principal
), getter_AddRefs(partitionedPrincipal
),
246 getter_AddRefs(loadGroup
));
247 NS_ENSURE_SUCCESS(rv
, rv
);
249 // Workers themselves can have their own CSP - Workers of an opaque origin
250 // however inherit the CSP of the document that spawned the worker.
251 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
252 if (CSP_ShouldResponseInheritCSP(aChannel
)) {
253 nsCOMPtr
<nsILoadInfo
> loadinfo
= aChannel
->LoadInfo();
254 csp
= loadinfo
->GetCsp();
256 return SetPrincipalsAndCSPOnMainThread(principal
, partitionedPrincipal
,
260 bool WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel
* aChannel
) {
261 AssertIsOnMainThread();
263 nsCOMPtr
<nsIPrincipal
> principal
;
264 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
265 nsCOMPtr
<nsILoadGroup
> loadGroup
;
266 nsresult rv
= GetPrincipalsAndLoadGroupFromChannel(
267 aChannel
, getter_AddRefs(principal
), getter_AddRefs(partitionedPrincipal
),
268 getter_AddRefs(loadGroup
));
269 NS_ENSURE_SUCCESS(rv
, false);
271 // Verify that the channel is still a null principal. We don't care
272 // if these are the exact same null principal object, though. From
273 // the worker's perspective its the same effect.
274 if (principal
->GetIsNullPrincipal() && mPrincipal
->GetIsNullPrincipal()) {
278 // Otherwise we require exact equality. Redirects can happen, but they
279 // are not allowed to change our principal.
280 if (principal
->Equals(mPrincipal
)) {
287 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
288 bool WorkerLoadInfo::PrincipalIsValid() const {
289 return mPrincipal
&& mPrincipalInfo
&&
290 mPrincipalInfo
->type() != PrincipalInfo::T__None
&&
291 mPrincipalInfo
->type() <= PrincipalInfo::T__Last
&&
292 mPartitionedPrincipal
&& mPartitionedPrincipalInfo
&&
293 mPartitionedPrincipalInfo
->type() != PrincipalInfo::T__None
&&
294 mPartitionedPrincipalInfo
->type() <= PrincipalInfo::T__Last
;
297 bool WorkerLoadInfo::PrincipalURIMatchesScriptURL() {
298 AssertIsOnMainThread();
300 nsAutoCString scheme
;
301 nsresult rv
= mBaseURI
->GetScheme(scheme
);
302 NS_ENSURE_SUCCESS(rv
, false);
304 // A system principal must either be a blob URL or a resource JSM.
305 if (mPrincipal
->IsSystemPrincipal()) {
306 if (scheme
== "blob"_ns
) {
310 bool isResource
= false;
311 nsresult rv
= NS_URIChainHasFlags(
312 mBaseURI
, nsIProtocolHandler::URI_IS_UI_RESOURCE
, &isResource
);
313 NS_ENSURE_SUCCESS(rv
, false);
318 // A null principal can occur for a data URL worker script or a blob URL
319 // worker script from a sandboxed iframe.
320 if (mPrincipal
->GetIsNullPrincipal()) {
321 return scheme
== "data"_ns
|| scheme
== "blob"_ns
;
324 // The principal for a blob: URL worker script does not have a matching URL.
325 // This is likely a bug in our referer setting logic, but exempt it for now.
326 // This is another reason we should fix bug 1340694 so that referer does not
327 // depend on the principal URI.
328 if (scheme
== "blob"_ns
) {
332 if (mPrincipal
->IsSameOrigin(mBaseURI
)) {
338 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
340 bool WorkerLoadInfo::ProxyReleaseMainThreadObjects(
341 WorkerPrivate
* aWorkerPrivate
) {
342 nsCOMPtr
<nsILoadGroup
> nullLoadGroup
;
343 return ProxyReleaseMainThreadObjects(aWorkerPrivate
,
344 std::move(nullLoadGroup
));
347 bool WorkerLoadInfo::ProxyReleaseMainThreadObjects(
348 WorkerPrivate
* aWorkerPrivate
,
349 nsCOMPtr
<nsILoadGroup
>&& aLoadGroupToCancel
) {
350 static const uint32_t kDoomedCount
= 11;
351 nsTArray
<nsCOMPtr
<nsISupports
>> doomed(kDoomedCount
);
353 SwapToISupportsArray(mWindow
, doomed
);
354 SwapToISupportsArray(mScriptContext
, doomed
);
355 SwapToISupportsArray(mBaseURI
, doomed
);
356 SwapToISupportsArray(mResolvedScriptURI
, doomed
);
357 SwapToISupportsArray(mPrincipal
, doomed
);
358 SwapToISupportsArray(mPartitionedPrincipal
, doomed
);
359 SwapToISupportsArray(mLoadingPrincipal
, doomed
);
360 SwapToISupportsArray(mChannel
, doomed
);
361 SwapToISupportsArray(mCSP
, doomed
);
362 SwapToISupportsArray(mLoadGroup
, doomed
);
363 SwapToISupportsArray(mInterfaceRequestor
, doomed
);
364 // Before adding anything here update kDoomedCount above!
366 MOZ_ASSERT(doomed
.Length() == kDoomedCount
);
368 RefPtr
<MainThreadReleaseRunnable
> runnable
= new MainThreadReleaseRunnable(
369 std::move(doomed
), std::move(aLoadGroupToCancel
));
370 return NS_SUCCEEDED(aWorkerPrivate
->DispatchToMainThread(runnable
.forget()));
373 WorkerLoadInfo::InterfaceRequestor::InterfaceRequestor(
374 nsIPrincipal
* aPrincipal
, nsILoadGroup
* aLoadGroup
) {
375 MOZ_ASSERT(NS_IsMainThread());
376 MOZ_ASSERT(aPrincipal
);
378 // Look for an existing LoadContext. This is optional and it's ok if
379 // we don't find one.
380 nsCOMPtr
<nsILoadContext
> baseContext
;
382 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
383 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
385 callbacks
->GetInterface(NS_GET_IID(nsILoadContext
),
386 getter_AddRefs(baseContext
));
388 mOuterRequestor
= callbacks
;
391 mLoadContext
= new LoadContext(aPrincipal
, baseContext
);
394 void WorkerLoadInfo::InterfaceRequestor::MaybeAddBrowserChild(
395 nsILoadGroup
* aLoadGroup
) {
396 MOZ_ASSERT(NS_IsMainThread());
402 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
403 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
408 nsCOMPtr
<nsIBrowserChild
> browserChild
;
409 callbacks
->GetInterface(NS_GET_IID(nsIBrowserChild
),
410 getter_AddRefs(browserChild
));
415 // Use weak references to the tab child. Holding a strong reference will
416 // not prevent an ActorDestroy() from being called on the BrowserChild.
417 // Therefore, we should let the BrowserChild destroy itself as soon as
419 mBrowserChildList
.AppendElement(do_GetWeakReference(browserChild
));
423 WorkerLoadInfo::InterfaceRequestor::GetInterface(const nsIID
& aIID
,
425 MOZ_ASSERT(NS_IsMainThread());
426 MOZ_ASSERT(mLoadContext
);
428 if (aIID
.Equals(NS_GET_IID(nsILoadContext
))) {
429 nsCOMPtr
<nsILoadContext
> ref
= mLoadContext
;
434 // If we still have an active nsIBrowserChild, then return it. Its possible,
435 // though, that all of the BrowserChild objects have been destroyed. In that
436 // case we return NS_NOINTERFACE.
437 if (aIID
.Equals(NS_GET_IID(nsIBrowserChild
))) {
438 nsCOMPtr
<nsIBrowserChild
> browserChild
= GetAnyLiveBrowserChild();
440 return NS_NOINTERFACE
;
442 browserChild
.forget(aSink
);
446 if (aIID
.Equals(NS_GET_IID(nsINetworkInterceptController
)) &&
448 // If asked for the network intercept controller, ask the outer requestor,
449 // which could be the docshell.
450 return mOuterRequestor
->GetInterface(aIID
, aSink
);
453 return NS_NOINTERFACE
;
456 already_AddRefed
<nsIBrowserChild
>
457 WorkerLoadInfo::InterfaceRequestor::GetAnyLiveBrowserChild() {
458 MOZ_ASSERT(NS_IsMainThread());
460 // Search our list of known BrowserChild objects for one that still exists.
461 while (!mBrowserChildList
.IsEmpty()) {
462 nsCOMPtr
<nsIBrowserChild
> browserChild
=
463 do_QueryReferent(mBrowserChildList
.LastElement());
465 // Does this tab child still exist? If so, return it. We are done. If the
466 // PBrowser actor is no longer useful, don't bother returning this tab.
468 !static_cast<BrowserChild
*>(browserChild
.get())->IsDestroyed()) {
469 return browserChild
.forget();
472 // Otherwise remove the stale weak reference and check the next one
473 mBrowserChildList
.RemoveLastElement();
479 NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor
)
480 NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor
)
481 NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor
,
482 nsIInterfaceRequestor
)
484 WorkerLoadInfo::WorkerLoadInfo() { MOZ_COUNT_CTOR(WorkerLoadInfo
); }
486 WorkerLoadInfo::WorkerLoadInfo(WorkerLoadInfo
&& aOther
) noexcept
487 : WorkerLoadInfoData(std::move(aOther
)) {
488 MOZ_COUNT_CTOR(WorkerLoadInfo
);
491 WorkerLoadInfo::~WorkerLoadInfo() { MOZ_COUNT_DTOR(WorkerLoadInfo
); }
494 } // namespace mozilla