Bug 1909074. Don't pass OFFSET_BY_ORIGIN to GetResultingTransformMatrix when it's...
[gecko.git] / dom / workers / WorkerLoadInfo.cpp
blob29fb69c2128db3a09eb25bd32909acaa8283ccd0
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 // If strict file origin policy is in effect, local files will always fail
337 // IsSameOrigin unless they are identical. Explicitly check file origin
338 // policy, in that case.
340 bool allowsRelaxedOriginPolicy = false;
341 rv = mPrincipal->AllowsRelaxStrictFileOriginPolicy(
342 mBaseURI, &allowsRelaxedOriginPolicy);
344 if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
345 NS_URIIsLocalFile(mBaseURI) &&
346 (NS_SUCCEEDED(rv) && allowsRelaxedOriginPolicy)) {
347 return true;
350 return false;
352 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
354 bool WorkerLoadInfo::ProxyReleaseMainThreadObjects(
355 WorkerPrivate* aWorkerPrivate) {
356 nsCOMPtr<nsILoadGroup> nullLoadGroup;
357 return ProxyReleaseMainThreadObjects(aWorkerPrivate,
358 std::move(nullLoadGroup));
361 bool WorkerLoadInfo::ProxyReleaseMainThreadObjects(
362 WorkerPrivate* aWorkerPrivate,
363 nsCOMPtr<nsILoadGroup>&& aLoadGroupToCancel) {
364 static const uint32_t kDoomedCount = 11;
365 nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
367 SwapToISupportsArray(mWindow, doomed);
368 SwapToISupportsArray(mScriptContext, doomed);
369 SwapToISupportsArray(mBaseURI, doomed);
370 SwapToISupportsArray(mResolvedScriptURI, doomed);
371 SwapToISupportsArray(mPrincipal, doomed);
372 SwapToISupportsArray(mPartitionedPrincipal, doomed);
373 SwapToISupportsArray(mLoadingPrincipal, doomed);
374 SwapToISupportsArray(mChannel, doomed);
375 SwapToISupportsArray(mCSP, doomed);
376 SwapToISupportsArray(mLoadGroup, doomed);
377 SwapToISupportsArray(mInterfaceRequestor, doomed);
378 // Before adding anything here update kDoomedCount above!
380 MOZ_ASSERT(doomed.Length() == kDoomedCount);
382 RefPtr<MainThreadReleaseRunnable> runnable = new MainThreadReleaseRunnable(
383 std::move(doomed), std::move(aLoadGroupToCancel));
384 return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
387 WorkerLoadInfo::InterfaceRequestor::InterfaceRequestor(
388 nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup) {
389 MOZ_ASSERT(NS_IsMainThread());
390 MOZ_ASSERT(aPrincipal);
392 // Look for an existing LoadContext. This is optional and it's ok if
393 // we don't find one.
394 nsCOMPtr<nsILoadContext> baseContext;
395 if (aLoadGroup) {
396 nsCOMPtr<nsIInterfaceRequestor> callbacks;
397 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
398 if (callbacks) {
399 callbacks->GetInterface(NS_GET_IID(nsILoadContext),
400 getter_AddRefs(baseContext));
402 mOuterRequestor = callbacks;
405 mLoadContext = new LoadContext(aPrincipal, baseContext);
408 void WorkerLoadInfo::InterfaceRequestor::MaybeAddBrowserChild(
409 nsILoadGroup* aLoadGroup) {
410 MOZ_ASSERT(NS_IsMainThread());
412 if (!aLoadGroup) {
413 return;
416 nsCOMPtr<nsIInterfaceRequestor> callbacks;
417 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
418 if (!callbacks) {
419 return;
422 nsCOMPtr<nsIBrowserChild> browserChild;
423 callbacks->GetInterface(NS_GET_IID(nsIBrowserChild),
424 getter_AddRefs(browserChild));
425 if (!browserChild) {
426 return;
429 // Use weak references to the tab child. Holding a strong reference will
430 // not prevent an ActorDestroy() from being called on the BrowserChild.
431 // Therefore, we should let the BrowserChild destroy itself as soon as
432 // possible.
433 mBrowserChildList.AppendElement(do_GetWeakReference(browserChild));
436 NS_IMETHODIMP
437 WorkerLoadInfo::InterfaceRequestor::GetInterface(const nsIID& aIID,
438 void** aSink) {
439 MOZ_ASSERT(NS_IsMainThread());
440 MOZ_ASSERT(mLoadContext);
442 if (aIID.Equals(NS_GET_IID(nsILoadContext))) {
443 nsCOMPtr<nsILoadContext> ref = mLoadContext;
444 ref.forget(aSink);
445 return NS_OK;
448 // If we still have an active nsIBrowserChild, then return it. Its possible,
449 // though, that all of the BrowserChild objects have been destroyed. In that
450 // case we return NS_NOINTERFACE.
451 if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
452 nsCOMPtr<nsIBrowserChild> browserChild = GetAnyLiveBrowserChild();
453 if (!browserChild) {
454 return NS_NOINTERFACE;
456 browserChild.forget(aSink);
457 return NS_OK;
460 if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
461 mOuterRequestor) {
462 // If asked for the network intercept controller, ask the outer requestor,
463 // which could be the docshell.
464 return mOuterRequestor->GetInterface(aIID, aSink);
467 return NS_NOINTERFACE;
470 already_AddRefed<nsIBrowserChild>
471 WorkerLoadInfo::InterfaceRequestor::GetAnyLiveBrowserChild() {
472 MOZ_ASSERT(NS_IsMainThread());
474 // Search our list of known BrowserChild objects for one that still exists.
475 while (!mBrowserChildList.IsEmpty()) {
476 nsCOMPtr<nsIBrowserChild> browserChild =
477 do_QueryReferent(mBrowserChildList.LastElement());
479 // Does this tab child still exist? If so, return it. We are done. If the
480 // PBrowser actor is no longer useful, don't bother returning this tab.
481 if (browserChild &&
482 !static_cast<BrowserChild*>(browserChild.get())->IsDestroyed()) {
483 return browserChild.forget();
486 // Otherwise remove the stale weak reference and check the next one
487 mBrowserChildList.RemoveLastElement();
490 return nullptr;
493 NS_IMPL_ADDREF(WorkerLoadInfo::InterfaceRequestor)
494 NS_IMPL_RELEASE(WorkerLoadInfo::InterfaceRequestor)
495 NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor,
496 nsIInterfaceRequestor)
498 WorkerLoadInfo::WorkerLoadInfo() { MOZ_COUNT_CTOR(WorkerLoadInfo); }
500 WorkerLoadInfo::WorkerLoadInfo(WorkerLoadInfo&& aOther) noexcept
501 : WorkerLoadInfoData(std::move(aOther)) {
502 MOZ_COUNT_CTOR(WorkerLoadInfo);
505 WorkerLoadInfo::~WorkerLoadInfo() { MOZ_COUNT_DTOR(WorkerLoadInfo); }
507 } // namespace dom
508 } // namespace mozilla