tor-browser

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

EarlyHintsService.cpp (5732B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et tw=80 : */
      3 
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "EarlyHintsService.h"
      9 #include "EarlyHintPreconnect.h"
     10 #include "EarlyHintPreloader.h"
     11 #include "mozilla/dom/LinkStyle.h"
     12 #include "mozilla/PreloadHashKey.h"
     13 #include "mozilla/StoragePrincipalHelper.h"
     14 #include "nsContentUtils.h"
     15 #include "nsIChannel.h"
     16 #include "nsICookieJarSettings.h"
     17 #include "nsILoadInfo.h"
     18 #include "nsIPrincipal.h"
     19 #include "nsNetUtil.h"
     20 #include "nsString.h"
     21 
     22 namespace mozilla::net {
     23 
     24 EarlyHintsService::EarlyHintsService()
     25    : mOngoingEarlyHints(new OngoingEarlyHints()) {}
     26 
     27 // implementing the destructor in the .cpp file to allow EarlyHintsService.h
     28 // not to include EarlyHintPreloader.h, decoupling the two files and hopefully
     29 // allow faster compile times
     30 EarlyHintsService::~EarlyHintsService() = default;
     31 
     32 void EarlyHintsService::EarlyHint(
     33    const nsACString& aLinkHeader, nsIURI* aBaseURI, nsIChannel* aChannel,
     34    const nsACString& aReferrerPolicy, const nsACString& aCSPHeader,
     35    dom::CanonicalBrowsingContext* aLoadingBrowsingContext) {
     36  mEarlyHintsCount++;
     37  if (mFirstEarlyHint.isNothing()) {
     38    mFirstEarlyHint.emplace(TimeStamp::NowLoRes());
     39  } else {
     40    // Only allow one early hint response with link headers. See
     41    // https://html.spec.whatwg.org/multipage/semantics.html#early-hints
     42    // > Note: Only the first early hint response served during the navigation
     43    // > is handled, and it is discarded if it is succeeded by a cross-origin
     44    // > redirect.
     45    return;
     46  }
     47 
     48  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
     49  // We only follow Early Hints sent on the main document. Make sure that we got
     50  // the main document channel here.
     51  if (loadInfo->GetExternalContentPolicyType() !=
     52      ExtContentPolicy::TYPE_DOCUMENT) {
     53    MOZ_ASSERT(false, "Early Hint on non-document channel");
     54    return;
     55  }
     56  nsCOMPtr<nsIPrincipal> principal;
     57  // We want to set the top-level document as the triggeringPrincipal for the
     58  // load of the sub-resources (image, font, fetch, script, style, fetch and in
     59  // the future maybe more). We can't use the `triggeringPrincipal` of the main
     60  // document channel, because it is the `systemPrincipal` for user initiated
     61  // loads. Same for the `LoadInfo::FindPrincipalToInherit(aChannel)`.
     62  //
     63  // On 3xx redirects of the main document to cross site locations, all Early
     64  // Hint preloads get cancelled as specified in the whatwg spec:
     65  //
     66  //   Note: Only the first early hint response served during the navigation is
     67  //   handled, and it is discarded if it is succeeded by a cross-origin
     68  //   redirect. [1]
     69  //
     70  // Therefore the channel doesn't need to change the principal for any reason
     71  // and has the correct principal for the whole lifetime.
     72  //
     73  // [1]: https://html.spec.whatwg.org/multipage/semantics.html#early-hints
     74  nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
     75      aChannel, getter_AddRefs(principal));
     76  NS_ENSURE_SUCCESS_VOID(rv);
     77 
     78  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
     79  if (NS_FAILED(
     80          loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)))) {
     81    return;
     82  }
     83 
     84  // TODO: find out why LinkHeaderParser uses utf16 and check if it can be
     85  //       changed to utf8
     86  auto linkHeaders = ParseLinkHeader(NS_ConvertUTF8toUTF16(aLinkHeader));
     87 
     88  for (auto& linkHeader : linkHeaders) {
     89    if (linkHeader.mRel.LowerCaseEqualsLiteral("preconnect")) {
     90      mLinkType |= dom::LinkStyle::ePRECONNECT;
     91      OriginAttributes originAttributes;
     92      StoragePrincipalHelper::GetOriginAttributesForNetworkState(
     93          aChannel, originAttributes);
     94      EarlyHintPreconnect::MaybePreconnect(linkHeader, aBaseURI,
     95                                           std::move(originAttributes));
     96    } else if (linkHeader.mRel.LowerCaseEqualsLiteral("preload")) {
     97      mLinkType |= dom::LinkStyle::ePRELOAD;
     98      EarlyHintPreloader::MaybeCreateAndInsertPreload(
     99          mOngoingEarlyHints, linkHeader, aBaseURI, principal,
    100          cookieJarSettings, aReferrerPolicy, aCSPHeader,
    101          loadInfo->GetBrowsingContextID(), aLoadingBrowsingContext, false);
    102    } else if (linkHeader.mRel.LowerCaseEqualsLiteral("modulepreload")) {
    103      mLinkType |= dom::LinkStyle::eMODULE_PRELOAD;
    104      EarlyHintPreloader::MaybeCreateAndInsertPreload(
    105          mOngoingEarlyHints, linkHeader, aBaseURI, principal,
    106          cookieJarSettings, aReferrerPolicy, aCSPHeader,
    107          loadInfo->GetBrowsingContextID(), aLoadingBrowsingContext, true);
    108    } else if (linkHeader.mRel.LowerCaseEqualsLiteral(
    109                   "compression-dictionary")) {
    110      mLinkType |= dom::LinkStyle::eCOMPRESSION_DICTIONARY;
    111      EarlyHintPreloader::MaybeCreateAndInsertPreload(
    112          mOngoingEarlyHints, linkHeader, aBaseURI, principal,
    113          cookieJarSettings, aReferrerPolicy, aCSPHeader,
    114          loadInfo->GetBrowsingContextID(), aLoadingBrowsingContext, true);
    115    }
    116  }
    117 }
    118 
    119 void EarlyHintsService::Cancel(const nsACString& aReason) {
    120  Reset();
    121  mOngoingEarlyHints->CancelAll(aReason);
    122 }
    123 
    124 void EarlyHintsService::RegisterLinksAndGetConnectArgs(
    125    dom::ContentParentId aCpId, nsTArray<EarlyHintConnectArgs>& aOutLinks) {
    126  mOngoingEarlyHints->RegisterLinksAndGetConnectArgs(aCpId, aOutLinks);
    127 }
    128 
    129 void EarlyHintsService::Reset() {
    130  if (mEarlyHintsCount == 0) {
    131    return;
    132  }
    133  // Reset telemetry counters and timestamps.
    134  mEarlyHintsCount = 0;
    135  mFirstEarlyHint = Nothing();
    136 }
    137 
    138 }  // namespace mozilla::net