tor-browser

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

ParentProcessDocumentChannel.cpp (11659B)


      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 "ParentProcessDocumentChannel.h"
      9 
     10 #include "mozilla/extensions/StreamFilterParent.h"
     11 #include "mozilla/net/ParentChannelWrapper.h"
     12 #include "mozilla/net/UrlClassifierCommon.h"
     13 #include "mozilla/StaticPrefs_extensions.h"
     14 #include "nsCRT.h"
     15 #include "nsDocShell.h"
     16 #include "nsIObserverService.h"
     17 #include "nsIClassifiedChannel.h"
     18 #include "nsIXULRuntime.h"
     19 #include "nsHttpHandler.h"
     20 #include "nsDocShellLoadState.h"
     21 
     22 extern mozilla::LazyLogModule gDocumentChannelLog;
     23 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
     24 
     25 namespace mozilla {
     26 namespace net {
     27 
     28 using RedirectToRealChannelPromise =
     29    typename PDocumentChannelParent::RedirectToRealChannelPromise;
     30 
     31 NS_IMPL_ISUPPORTS_INHERITED(ParentProcessDocumentChannel, DocumentChannel,
     32                            nsIAsyncVerifyRedirectCallback, nsIObserver)
     33 
     34 ParentProcessDocumentChannel::ParentProcessDocumentChannel(
     35    nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
     36    nsLoadFlags aLoadFlags, uint32_t aCacheKey, bool aUriModified,
     37    bool aIsEmbeddingBlockedError)
     38    : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
     39                      aUriModified, aIsEmbeddingBlockedError) {
     40  LOG(("ParentProcessDocumentChannel ctor [this=%p]", this));
     41 }
     42 
     43 ParentProcessDocumentChannel::~ParentProcessDocumentChannel() {
     44  LOG(("ParentProcessDocumentChannel dtor [this=%p]", this));
     45 }
     46 
     47 RefPtr<RedirectToRealChannelPromise>
     48 ParentProcessDocumentChannel::RedirectToRealChannel(
     49    nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
     50        aStreamFilterEndpoints,
     51    uint32_t aRedirectFlags, uint32_t aLoadFlags,
     52    const nsTArray<EarlyHintConnectArgs>& aEarlyHints) {
     53  LOG(("ParentProcessDocumentChannel RedirectToRealChannel [this=%p]", this));
     54  nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
     55  channel->SetLoadFlags(aLoadFlags);
     56  channel->SetNotificationCallbacks(mCallbacks);
     57 
     58  if (mLoadGroup) {
     59    channel->SetLoadGroup(mLoadGroup);
     60  }
     61 
     62  if (XRE_IsE10sParentProcess()) {
     63    nsCOMPtr<nsIURI> uri;
     64    MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(channel, getter_AddRefs(uri)));
     65    if (!nsDocShell::CanLoadInParentProcess(uri)) {
     66      nsAutoCString msg;
     67      uri->GetSpec(msg);
     68      msg.Insert(
     69          "Attempt to load a non-authorised load in the parent process: ", 0);
     70      NS_ASSERTION(false, msg.get());
     71      return RedirectToRealChannelPromise::CreateAndResolve(
     72          NS_ERROR_CONTENT_BLOCKED, __func__);
     73    }
     74  }
     75  mStreamFilterEndpoints = std::move(aStreamFilterEndpoints);
     76 
     77  if (mDocumentLoadListener->IsDocumentLoad() &&
     78      mozilla::SessionHistoryInParent() && GetDocShell() &&
     79      mDocumentLoadListener->GetLoadingSessionHistoryInfo()) {
     80    GetDocShell()->SetLoadingSessionHistoryInfo(
     81        *mDocumentLoadListener->GetLoadingSessionHistoryInfo());
     82  }
     83 
     84  RefPtr<RedirectToRealChannelPromise> p = mPromise.Ensure(__func__);
     85  // We make the promise use direct task dispatch in order to reduce the number
     86  // of event loops iterations.
     87  mPromise.UseDirectTaskDispatch(__func__);
     88 
     89  nsresult rv =
     90      gHttpHandler->AsyncOnChannelRedirect(this, channel, aRedirectFlags);
     91  if (NS_FAILED(rv)) {
     92    LOG(
     93        ("ParentProcessDocumentChannel RedirectToRealChannel "
     94         "AsyncOnChannelRedirect failed [this=%p "
     95         "aRv=%d]",
     96         this, int(rv)));
     97    OnRedirectVerifyCallback(rv);
     98  }
     99 
    100  return p;
    101 }
    102 
    103 NS_IMETHODIMP
    104 ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
    105  LOG(
    106      ("ParentProcessDocumentChannel OnRedirectVerifyCallback [this=%p "
    107       "aResult=%d]",
    108       this, int(aResult)));
    109 
    110  MOZ_ASSERT(mDocumentLoadListener);
    111 
    112  if (NS_FAILED(aResult)) {
    113    Cancel(aResult);
    114  } else if (mCanceled) {
    115    aResult = NS_ERROR_ABORT;
    116  } else {
    117    const nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
    118    mLoadGroup->AddRequest(channel, nullptr);
    119    // Adding the channel to the loadgroup could have triggered a status
    120    // change with an observer being called destroying the docShell, resulting
    121    // in the PPDC to be canceled.
    122    if (mCanceled) {
    123      aResult = NS_ERROR_ABORT;
    124    } else {
    125      mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
    126      for (auto& endpoint : mStreamFilterEndpoints) {
    127        extensions::StreamFilterParent::Attach(channel, std::move(endpoint));
    128      }
    129 
    130      RefPtr<ParentChannelWrapper> wrapper =
    131          new ParentChannelWrapper(channel, mListener);
    132 
    133      wrapper->Register(mDocumentLoadListener->GetRedirectChannelId());
    134    }
    135  }
    136 
    137  mPromise.Resolve(aResult, __func__);
    138 
    139  return NS_OK;
    140 }
    141 
    142 NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
    143    nsIStreamListener* aListener) {
    144  LOG(("ParentProcessDocumentChannel AsyncOpen [this=%p]", this));
    145  auto docShell = RefPtr<nsDocShell>(GetDocShell());
    146  MOZ_ASSERT(docShell);
    147 
    148  bool isDocumentLoad = mLoadInfo->GetExternalContentPolicyType() !=
    149                        ExtContentPolicy::TYPE_OBJECT;
    150 
    151  mDocumentLoadListener = MakeRefPtr<DocumentLoadListener>(
    152      docShell->GetBrowsingContext()->Canonical(), isDocumentLoad);
    153  LOG(("Created PPDocumentChannel with listener=%p",
    154       mDocumentLoadListener.get()));
    155 
    156  // Add observers.
    157  nsCOMPtr<nsIObserverService> observerService =
    158      mozilla::services::GetObserverService();
    159  if (observerService) {
    160    MOZ_ALWAYS_SUCCEEDS(observerService->AddObserver(
    161        this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC, false));
    162  }
    163 
    164  gHttpHandler->OnOpeningDocumentRequest(this);
    165 
    166  if (isDocumentLoad) {
    167    // Return value of setting synced field should be checked. See bug 1656492.
    168    (void)GetDocShell()->GetBrowsingContext()->SetCurrentLoadIdentifier(
    169        Some(mLoadState->GetLoadIdentifier()));
    170  }
    171 
    172  nsresult rv = NS_OK;
    173  Maybe<dom::ClientInfo> initialClientInfo = mInitialClientInfo;
    174 
    175  RefPtr<DocumentLoadListener::OpenPromise> promise;
    176  if (isDocumentLoad) {
    177    promise = mDocumentLoadListener->OpenDocument(
    178        mLoadState, mLoadFlags, mCacheKey, Some(mChannelId), TimeStamp::Now(),
    179        mTiming, std::move(initialClientInfo), mUriModified,
    180        Some(mIsEmbeddingBlockedError), nullptr /* ContentParent */, &rv);
    181  } else {
    182    promise = mDocumentLoadListener->OpenObject(
    183        mLoadState, mCacheKey, Some(mChannelId), TimeStamp::Now(), mTiming,
    184        std::move(initialClientInfo), InnerWindowIDForExtantDoc(docShell),
    185        mLoadFlags, mLoadInfo->InternalContentPolicyType(),
    186        dom::UserActivation::IsHandlingUserInput(), nullptr /* ContentParent */,
    187        nullptr /* ObjectUpgradeHandler */, &rv);
    188  }
    189 
    190  if (NS_FAILED(rv)) {
    191    MOZ_ASSERT(!promise);
    192    mDocumentLoadListener = nullptr;
    193    RemoveObserver();
    194    return rv;
    195  }
    196 
    197  mListener = aListener;
    198  if (mLoadGroup) {
    199    mLoadGroup->AddRequest(this, nullptr);
    200  }
    201 
    202  RefPtr<ParentProcessDocumentChannel> self = this;
    203  promise->Then(
    204      GetCurrentSerialEventTarget(), __func__,
    205      [self](DocumentLoadListener::OpenPromiseSucceededType&& aResolveValue) {
    206        self->mDocumentLoadListener->CancelEarlyHintPreloads();
    207        nsTArray<EarlyHintConnectArgs> earlyHints;
    208 
    209        // The DLL is waiting for us to resolve the
    210        // RedirectToRealChannelPromise given as parameter.
    211        RefPtr<RedirectToRealChannelPromise> p =
    212            self->RedirectToRealChannel(
    213                    std::move(aResolveValue.mStreamFilterEndpoints),
    214                    aResolveValue.mRedirectFlags, aResolveValue.mLoadFlags,
    215                    earlyHints)
    216                ->Then(
    217                    GetCurrentSerialEventTarget(), __func__,
    218                    [self](RedirectToRealChannelPromise::ResolveOrRejectValue&&
    219                               aValue) -> RefPtr<RedirectToRealChannelPromise> {
    220                      MOZ_ASSERT(aValue.IsResolve());
    221                      nsresult rv = aValue.ResolveValue();
    222                      if (NS_FAILED(rv)) {
    223                        self->DisconnectChildListeners(rv, rv);
    224                      }
    225                      self->mLoadGroup = nullptr;
    226                      self->mListener = nullptr;
    227                      self->mCallbacks = nullptr;
    228                      self->RemoveObserver();
    229                      auto p =
    230                          MakeRefPtr<RedirectToRealChannelPromise::Private>(
    231                              __func__);
    232                      p->UseDirectTaskDispatch(__func__);
    233                      p->ResolveOrReject(std::move(aValue), __func__);
    234                      return p;
    235                    });
    236        // We chain the promise the DLL is waiting on to the one returned by
    237        // RedirectToRealChannel. As soon as the promise returned is
    238        // resolved or rejected, so will the DLL's promise.
    239        p->ChainTo(aResolveValue.mPromise.forget(), __func__);
    240      },
    241      [self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
    242        // If this is a normal failure, then we want to disconnect our listeners
    243        // and notify them of the failure. If this is a process switch, then we
    244        // can just ignore it silently, and trust that the switch will shut down
    245        // our docshell and cancel us when it's ready.
    246        if (!aRejectValue.mContinueNavigating) {
    247          self->DisconnectChildListeners(aRejectValue.mStatus,
    248                                         aRejectValue.mLoadGroupStatus);
    249        }
    250        self->RemoveObserver();
    251      });
    252  return NS_OK;
    253 }
    254 
    255 NS_IMETHODIMP ParentProcessDocumentChannel::Cancel(nsresult aStatus) {
    256  return CancelWithReason(aStatus, "ParentProcessDocumentChannel::Cancel"_ns);
    257 }
    258 
    259 NS_IMETHODIMP ParentProcessDocumentChannel::CancelWithReason(
    260    nsresult aStatusCode, const nsACString& aReason) {
    261  LOG(("ParentProcessDocumentChannel CancelWithReason [this=%p]", this));
    262  if (mCanceled) {
    263    return NS_OK;
    264  }
    265 
    266  mCanceled = true;
    267  // This will force the DocumentListener to abort the promise if there's one
    268  // pending.
    269  mDocumentLoadListener->Cancel(aStatusCode, aReason);
    270 
    271  return NS_OK;
    272 }
    273 
    274 void ParentProcessDocumentChannel::RemoveObserver() {
    275  if (nsCOMPtr<nsIObserverService> observerService =
    276          mozilla::services::GetObserverService()) {
    277    observerService->RemoveObserver(this, NS_HTTP_ON_MODIFY_REQUEST_TOPIC);
    278  }
    279 }
    280 
    281 ////////////////////////////////////////////////////////////////////////////////
    282 // nsIObserver
    283 ////////////////////////////////////////////////////////////////////////////////
    284 
    285 NS_IMETHODIMP
    286 ParentProcessDocumentChannel::Observe(nsISupports* aSubject, const char* aTopic,
    287                                      const char16_t* aData) {
    288  MOZ_ASSERT(NS_IsMainThread());
    289 
    290  if (mRequestObserversCalled) {
    291    // We have already emitted the event, we don't want to emit it again.
    292    // We only care about forwarding the first NS_HTTP_ON_MODIFY_REQUEST_TOPIC
    293    // encountered.
    294    return NS_OK;
    295  }
    296  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aSubject);
    297  if (!channel || mDocumentLoadListener->GetChannel() != channel) {
    298    // Not a channel we are interested with.
    299    return NS_OK;
    300  }
    301  LOG(("DocumentChannelParent Observe [this=%p aChannel=%p]", this,
    302       channel.get()));
    303  if (!nsCRT::strcmp(aTopic, NS_HTTP_ON_MODIFY_REQUEST_TOPIC)) {
    304    mRequestObserversCalled = true;
    305    gHttpHandler->OnModifyDocumentRequest(this);
    306  }
    307 
    308  return NS_OK;
    309 }
    310 
    311 }  // namespace net
    312 }  // namespace mozilla
    313 
    314 #undef LOG