Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / dom / workers / WorkerLoadInfo.cpp
blobde86f19713bef42a5a02dae349a23c32539ee7da
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"
29 namespace mozilla {
31 using namespace ipc;
33 namespace dom {
35 namespace {
37 class MainThreadReleaseRunnable final : public Runnable {
38 nsTArray<nsCOMPtr<nsISupports>> mDoomed;
39 nsCOMPtr<nsILoadGroup> mLoadGroupToCancel;
41 public:
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)
50 NS_IMETHOD
51 Run() override {
52 if (mLoadGroupToCancel) {
53 mLoadGroupToCancel->CancelWithReason(
54 NS_BINDING_ABORTED, "WorkerLoadInfo::MainThreadReleaseRunnable"_ns);
55 mLoadGroupToCancel = nullptr;
58 mDoomed.Clear();
59 return NS_OK;
62 private:
63 ~MainThreadReleaseRunnable() = default;
66 // Specialize this if there's some class that has multiple nsISupports bases.
67 template <class T>
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();
77 T* raw = nullptr;
78 aSrc.swap(raw);
80 nsISupports* rawSupports =
81 static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
82 dest->swap(rawSupports);
85 } // namespace
87 WorkerLoadInfoData::WorkerLoadInfoData()
88 : mLoadFlags(nsIRequest::LOAD_NORMAL),
89 mWindowID(UINT64_MAX),
90 mAssociatedBrowsingContextID(0),
91 mReferrerInfo(new ReferrerInfo(nullptr)),
92 mFromWindow(false),
93 mEvalAllowed(false),
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;
116 mCSP = aCsp;
118 if (mCSP) {
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))) {
124 return rv;
126 } else {
127 mEvalAllowed = true;
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;
145 } else {
146 mPartitionedPrincipalInfo = MakeUnique<PrincipalInfo>();
147 rv = PrincipalToPrincipalInfo(aPartitionedPrincipal,
148 mPartitionedPrincipalInfo.get());
149 NS_ENSURE_SUCCESS(rv, rv);
151 return NS_OK;
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.
211 bool isResource;
212 rv = NS_URIChainHasFlags(finalURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
213 &isResource);
214 NS_ENSURE_SUCCESS(rv, rv);
216 if (isResource) {
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;
221 } else {
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);
235 return NS_OK;
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,
257 loadGroup, csp);
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()) {
275 return true;
278 // Otherwise we require exact equality. Redirects can happen, but they
279 // are not allowed to change our principal.
280 if (principal->Equals(mPrincipal)) {
281 return true;
284 return false;
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) {
307 return true;
310 bool isResource = false;
311 nsresult rv = NS_URIChainHasFlags(
312 mBaseURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isResource);
313 NS_ENSURE_SUCCESS(rv, false);
315 return isResource;
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) {
329 return true;
332 if (mPrincipal->IsSameOrigin(mBaseURI)) {
333 return true;
336 return false;
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;
381 if (aLoadGroup) {
382 nsCOMPtr<nsIInterfaceRequestor> callbacks;
383 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
384 if (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());
398 if (!aLoadGroup) {
399 return;
402 nsCOMPtr<nsIInterfaceRequestor> callbacks;
403 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
404 if (!callbacks) {
405 return;
408 nsCOMPtr<nsIBrowserChild> browserChild;
409 callbacks->GetInterface(NS_GET_IID(nsIBrowserChild),
410 getter_AddRefs(browserChild));
411 if (!browserChild) {
412 return;
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
418 // possible.
419 mBrowserChildList.AppendElement(do_GetWeakReference(browserChild));
422 NS_IMETHODIMP
423 WorkerLoadInfo::InterfaceRequestor::GetInterface(const nsIID& aIID,
424 void** aSink) {
425 MOZ_ASSERT(NS_IsMainThread());
426 MOZ_ASSERT(mLoadContext);
428 if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
429 nsCOMPtr<nsILoadContext> ref = mLoadContext;
430 ref.forget(aSink);
431 return NS_OK;
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();
439 if (!browserChild) {
440 return NS_NOINTERFACE;
442 browserChild.forget(aSink);
443 return NS_OK;
446 if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
447 mOuterRequestor) {
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.
467 if (browserChild &&
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();
476 return nullptr;
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); }
493 } // namespace dom
494 } // namespace mozilla