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 "ServiceWorkerInterceptController.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/StaticPrefs_dom.h"
11 #include "mozilla/StaticPrefs_privacy.h"
12 #include "mozilla/StorageAccess.h"
13 #include "mozilla/StoragePrincipalHelper.h"
14 #include "mozilla/dom/CanonicalBrowsingContext.h"
15 #include "mozilla/dom/InternalRequest.h"
16 #include "mozilla/net/HttpBaseChannel.h"
18 #include "nsContentUtils.h"
19 #include "nsIChannel.h"
20 #include "nsICookieJarSettings.h"
21 #include "ServiceWorkerManager.h"
22 #include "nsIPrincipal.h"
23 #include "nsQueryObject.h"
25 namespace mozilla::dom
{
28 bool IsWithinObjectOrEmbed(const nsCOMPtr
<nsILoadInfo
>& loadInfo
) {
29 RefPtr
<BrowsingContext
> browsingContext
;
30 loadInfo
->GetTargetBrowsingContext(getter_AddRefs(browsingContext
));
32 for (BrowsingContext
* cur
= browsingContext
.get(); cur
;
33 cur
= cur
->GetParent()) {
34 if (cur
->IsEmbedderTypeObjectOrEmbed()) {
43 NS_IMPL_ISUPPORTS(ServiceWorkerInterceptController
,
44 nsINetworkInterceptController
)
47 ServiceWorkerInterceptController::ShouldPrepareForIntercept(
48 nsIURI
* aURI
, nsIChannel
* aChannel
, bool* aShouldIntercept
) {
49 *aShouldIntercept
= false;
51 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
53 // Block interception if the request's destination is within an object or
55 if (IsWithinObjectOrEmbed(loadInfo
)) {
59 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
61 // For subresource requests we base our decision solely on the client's
62 // controller value. Any settings that would have blocked service worker
63 // access should have been set before the initial navigation created the
65 if (!nsContentUtils::IsNonSubresourceRequest(aChannel
)) {
66 const Maybe
<ServiceWorkerDescriptor
>& controller
=
67 loadInfo
->GetController();
69 // If the controller doesn't handle fetch events, return false
70 if (!controller
.isSome()) {
74 *aShouldIntercept
= controller
.ref().HandlesFetch();
76 // The service worker has no fetch event handler, try to schedule a
77 // soft-update through ServiceWorkerRegistrationInfo.
78 // Get ServiceWorkerRegistrationInfo by the ServiceWorkerInfo's principal
80 if (!*aShouldIntercept
&& swm
) {
81 nsCOMPtr
<nsIPrincipal
> principal
=
82 controller
.ref().GetPrincipal().unwrap();
83 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
84 swm
->GetRegistration(principal
, controller
.ref().Scope());
85 // Could not get ServiceWorkerRegistration here if unregister is
86 // executed before getting here.
87 if (NS_WARN_IF(!registration
)) {
90 registration
->MaybeScheduleTimeCheckAndUpdate();
93 RefPtr
<net::HttpBaseChannel
> httpChannel
= do_QueryObject(aChannel
);
96 httpChannel
->GetRequestHead()->HasHeader(net::nsHttp::Range
)) {
97 RequestMode requestMode
=
98 InternalRequest::MapChannelToRequestMode(aChannel
);
99 bool mayLoad
= nsContentUtils::CheckMayLoad(
100 loadInfo
->GetLoadingPrincipal(), aChannel
,
101 /*allowIfInheritsPrincipal*/ false);
102 if (requestMode
== RequestMode::No_cors
&& !mayLoad
) {
103 *aShouldIntercept
= false;
110 nsCOMPtr
<nsIPrincipal
> principal
;
111 nsresult rv
= StoragePrincipalHelper::GetPrincipal(
113 StaticPrefs::privacy_partition_serviceWorkers()
114 ? StoragePrincipalHelper::eForeignPartitionedPrincipal
115 : StoragePrincipalHelper::eRegularPrincipal
,
116 getter_AddRefs(principal
));
117 NS_ENSURE_SUCCESS(rv
, rv
);
119 // First check with the ServiceWorkerManager for a matching service worker.
120 if (!swm
|| !swm
->IsAvailable(principal
, aURI
, aChannel
)) {
124 // Check if we're in a secure context, unless service worker testing is
126 if (!nsContentUtils::ComputeIsSecureContext(aChannel
) &&
127 !StaticPrefs::dom_serviceWorkers_testing_enabled()) {
131 // Then check to see if we are allowed to control the window.
132 // It is important to check for the availability of the service worker first
133 // to avoid showing warnings about the use of third-party cookies in the UI
134 // unnecessarily when no service worker is being accessed.
135 auto storageAccess
= StorageAllowedForChannel(aChannel
);
136 if (storageAccess
!= StorageAccess::eAllow
) {
137 if (!StaticPrefs::privacy_partition_serviceWorkers()) {
141 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
142 loadInfo
->GetCookieJarSettings(getter_AddRefs(cookieJarSettings
));
144 if (!StoragePartitioningEnabled(storageAccess
, cookieJarSettings
)) {
149 *aShouldIntercept
= true;
154 ServiceWorkerInterceptController::ChannelIntercepted(
155 nsIInterceptedChannel
* aChannel
) {
156 // Note, do not cancel the interception here. The caller will try to
157 // ResetInterception() on error.
159 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
161 return NS_ERROR_FAILURE
;
165 swm
->DispatchFetchEvent(aChannel
, error
);
166 if (NS_WARN_IF(error
.Failed())) {
167 return error
.StealNSResult();
173 } // namespace mozilla::dom