tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ServiceWorkerInterceptController.cpp (5964B)


      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/. */
      6 
      7 #include "ServiceWorkerInterceptController.h"
      8 
      9 #include "ServiceWorkerManager.h"
     10 #include "mozilla/BasePrincipal.h"
     11 #include "mozilla/StaticPrefs_dom.h"
     12 #include "mozilla/StaticPrefs_privacy.h"
     13 #include "mozilla/StorageAccess.h"
     14 #include "mozilla/StoragePrincipalHelper.h"
     15 #include "mozilla/dom/CanonicalBrowsingContext.h"
     16 #include "mozilla/dom/InternalRequest.h"
     17 #include "mozilla/net/HttpBaseChannel.h"
     18 #include "nsCOMPtr.h"
     19 #include "nsContentUtils.h"
     20 #include "nsIChannel.h"
     21 #include "nsICookieJarSettings.h"
     22 #include "nsIPrincipal.h"
     23 #include "nsQueryObject.h"
     24 
     25 namespace mozilla::dom {
     26 
     27 namespace {
     28 bool IsWithinObjectOrEmbed(const nsCOMPtr<nsILoadInfo>& loadInfo) {
     29  RefPtr<BrowsingContext> browsingContext;
     30  loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext));
     31 
     32  for (BrowsingContext* cur = browsingContext.get(); cur;
     33       cur = cur->GetParent()) {
     34    if (cur->IsEmbedderTypeObjectOrEmbed()) {
     35      return true;
     36    }
     37  }
     38 
     39  return false;
     40 }
     41 }  // namespace
     42 
     43 NS_IMPL_ISUPPORTS(ServiceWorkerInterceptController,
     44                  nsINetworkInterceptController)
     45 
     46 NS_IMETHODIMP
     47 ServiceWorkerInterceptController::ShouldPrepareForIntercept(
     48    nsIURI* aURI, nsIChannel* aChannel, bool* aShouldIntercept) {
     49  *aShouldIntercept = false;
     50 
     51  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     52 
     53  // Block interception if the request's destination is within an object or
     54  // embed element.
     55  if (IsWithinObjectOrEmbed(loadInfo)) {
     56    return NS_OK;
     57  }
     58 
     59  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     60 
     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
     64  // window.
     65  if (!nsContentUtils::IsNonSubresourceRequest(aChannel)) {
     66    const Maybe<ServiceWorkerDescriptor>& controller =
     67        loadInfo->GetController();
     68 
     69    // If the controller doesn't handle fetch events, return false
     70    if (!controller.isSome()) {
     71      return NS_OK;
     72    }
     73 
     74    *aShouldIntercept = controller.ref().HandlesFetch();
     75 
     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
     79    // and scope
     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)) {
     88        return NS_OK;
     89      }
     90      registration->MaybeScheduleTimeCheckAndUpdate();
     91    }
     92 
     93    RefPtr<net::HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
     94 
     95    if (httpChannel &&
     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;
    104      }
    105    }
    106 
    107    return NS_OK;
    108  }
    109 
    110  nsCOMPtr<nsIPrincipal> principal;
    111  nsresult rv = StoragePrincipalHelper::GetPrincipal(
    112      aChannel,
    113      StaticPrefs::privacy_partition_serviceWorkers()
    114          ? StoragePrincipalHelper::eForeignPartitionedPrincipal
    115          : StoragePrincipalHelper::eRegularPrincipal,
    116      getter_AddRefs(principal));
    117  NS_ENSURE_SUCCESS(rv, rv);
    118 
    119  // First check with the ServiceWorkerManager for a matching service worker.
    120  if (!swm || !swm->IsAvailable(principal, aURI, aChannel)) {
    121    return NS_OK;
    122  }
    123 
    124  // Check if we're in a secure context, unless service worker testing is
    125  // enabled.
    126  if (!nsContentUtils::ComputeIsSecureContext(aChannel) &&
    127      !StaticPrefs::dom_serviceWorkers_testing_enabled()) {
    128    return NS_OK;
    129  }
    130 
    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  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
    137  loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
    138 
    139  *aShouldIntercept =
    140      storageAccess == StorageAccess::eAllow ||
    141      (storageAccess == StorageAccess::ePrivateBrowsing &&
    142       StaticPrefs::dom_serviceWorkers_privateBrowsing_enabled()) ||
    143      (ShouldPartitionStorage(storageAccess) &&
    144       StaticPrefs::privacy_partition_serviceWorkers() &&
    145       StoragePartitioningEnabled(storageAccess, cookieJarSettings) &&
    146       (!principal->GetIsInPrivateBrowsing() ||
    147        StaticPrefs::dom_serviceWorkers_privateBrowsing_enabled()));
    148  return NS_OK;
    149 }
    150 
    151 NS_IMETHODIMP
    152 ServiceWorkerInterceptController::ChannelIntercepted(
    153    nsIInterceptedChannel* aChannel) {
    154  // Note, do not cancel the interception here.  The caller will try to
    155  // ResetInterception() on error.
    156 
    157  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    158  if (!swm) {
    159    return NS_ERROR_FAILURE;
    160  }
    161 
    162  ErrorResult error;
    163  swm->DispatchFetchEvent(aChannel, error);
    164  if (NS_WARN_IF(error.Failed())) {
    165    return error.StealNSResult();
    166  }
    167 
    168  return NS_OK;
    169 }
    170 
    171 }  // namespace mozilla::dom