tor-browser

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

DocumentChannelChild.cpp (16791B)


      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 "DocumentChannelChild.h"
      9 
     10 #include "mozilla/dom/Document.h"
     11 #include "mozilla/dom/PolicyContainer.h"
     12 #include "mozilla/dom/RemoteType.h"
     13 #include "mozilla/extensions/StreamFilterParent.h"
     14 #include "mozilla/ipc/Endpoint.h"
     15 #include "mozilla/net/HttpBaseChannel.h"
     16 #include "mozilla/net/NeckoChild.h"
     17 #include "mozilla/ScopeExit.h"
     18 #include "mozilla/StaticPrefs_fission.h"
     19 #include "nsHashPropertyBag.h"
     20 #include "nsIHttpChannelInternal.h"
     21 #include "nsIObjectLoadingContent.h"
     22 #include "nsIXULRuntime.h"
     23 #include "nsIWritablePropertyBag.h"
     24 #include "nsFrameLoader.h"
     25 #include "nsFrameLoaderOwner.h"
     26 #include "nsQueryObject.h"
     27 #include "nsDocShellLoadState.h"
     28 
     29 using namespace mozilla::dom;
     30 using namespace mozilla::ipc;
     31 
     32 extern mozilla::LazyLogModule gDocumentChannelLog;
     33 #define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
     34 
     35 namespace mozilla {
     36 namespace net {
     37 
     38 //-----------------------------------------------------------------------------
     39 // DocumentChannelChild::nsISupports
     40 
     41 NS_INTERFACE_MAP_BEGIN(DocumentChannelChild)
     42  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
     43 NS_INTERFACE_MAP_END_INHERITING(DocumentChannel)
     44 
     45 NS_IMPL_ADDREF_INHERITED(DocumentChannelChild, DocumentChannel)
     46 NS_IMPL_RELEASE_INHERITED(DocumentChannelChild, DocumentChannel)
     47 
     48 DocumentChannelChild::DocumentChannelChild(nsDocShellLoadState* aLoadState,
     49                                           net::LoadInfo* aLoadInfo,
     50                                           nsLoadFlags aLoadFlags,
     51                                           uint32_t aCacheKey,
     52                                           bool aUriModified,
     53                                           bool aIsEmbeddingBlockedError)
     54    : DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey,
     55                      aUriModified, aIsEmbeddingBlockedError) {
     56  mLoadingContext = nullptr;
     57  LOG(("DocumentChannelChild ctor [this=%p, uri=%s]", this,
     58       aLoadState->URI()->GetSpecOrDefault().get()));
     59 }
     60 
     61 DocumentChannelChild::~DocumentChannelChild() {
     62  LOG(("DocumentChannelChild dtor [this=%p]", this));
     63 }
     64 
     65 NS_IMETHODIMP
     66 DocumentChannelChild::AsyncOpen(nsIStreamListener* aListener) {
     67  nsresult rv = NS_OK;
     68 
     69  nsCOMPtr<nsIStreamListener> listener = aListener;
     70 
     71  NS_ENSURE_TRUE(gNeckoChild, NS_ERROR_FAILURE);
     72  NS_ENSURE_ARG_POINTER(listener);
     73  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     74  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
     75 
     76  // Port checked in parent, but duplicate here so we can return with error
     77  // immediately, as we've done since before e10s.
     78  rv = NS_CheckPortSafety(mURI);
     79  NS_ENSURE_SUCCESS(rv, rv);
     80 
     81  bool isNotDownload = mLoadState->FileName().IsVoid();
     82 
     83  // If not a download, add ourselves to the load group
     84  if (isNotDownload && mLoadGroup) {
     85    // During this call, we can re-enter back into the DocumentChannelChild to
     86    // call SetNavigationTiming.
     87    mLoadGroup->AddRequest(this, nullptr);
     88  }
     89 
     90  if (mCanceled) {
     91    // We may have been canceled already, either by on-modify-request
     92    // listeners or by load group observers; in that case, don't create IPDL
     93    // connection. See nsHttpChannel::AsyncOpen().
     94    return mStatus;
     95  }
     96 
     97  gHttpHandler->OnOpeningDocumentRequest(this);
     98 
     99  RefPtr<nsDocShell> docShell = GetDocShell();
    100  if (!docShell) {
    101    return NS_ERROR_FAILURE;
    102  }
    103 
    104  // `loadingContext` is the BC that is initiating the resource load.
    105  // For normal subdocument loads, the BC is the one that the subdoc will load
    106  // into. For <object>/<embed> it's the embedder doc's BC.
    107  RefPtr<BrowsingContext> loadingContext = docShell->GetBrowsingContext();
    108  if (!loadingContext || loadingContext->IsDiscarded()) {
    109    return NS_ERROR_FAILURE;
    110  }
    111  mLoadingContext = loadingContext;
    112 
    113  Maybe<IPCClientInfo> ipcClientInfo;
    114  if (mInitialClientInfo.isSome()) {
    115    ipcClientInfo.emplace(mInitialClientInfo.ref().ToIPC());
    116  }
    117 
    118  DocumentChannelElementCreationArgs ipcElementCreationArgs;
    119  switch (mLoadInfo->GetExternalContentPolicyType()) {
    120    case ExtContentPolicy::TYPE_DOCUMENT:
    121    case ExtContentPolicy::TYPE_SUBDOCUMENT: {
    122      DocumentCreationArgs docArgs;
    123      docArgs.loadFlags() = mLoadFlags;
    124      docArgs.uriModified() = mUriModified;
    125      docArgs.isEmbeddingBlockedError() = mIsEmbeddingBlockedError;
    126 
    127      ipcElementCreationArgs = docArgs;
    128      break;
    129    }
    130 
    131    case ExtContentPolicy::TYPE_OBJECT: {
    132      ObjectCreationArgs objectArgs;
    133      objectArgs.embedderInnerWindowId() = InnerWindowIDForExtantDoc(docShell);
    134      objectArgs.loadFlags() = mLoadFlags;
    135      objectArgs.contentPolicyType() = mLoadInfo->InternalContentPolicyType();
    136      objectArgs.isUrgentStart() = UserActivation::IsHandlingUserInput();
    137 
    138      ipcElementCreationArgs = objectArgs;
    139      break;
    140    }
    141 
    142    default:
    143      MOZ_ASSERT_UNREACHABLE("unsupported content policy type");
    144      return NS_ERROR_FAILURE;
    145  }
    146 
    147  switch (mLoadInfo->GetExternalContentPolicyType()) {
    148    case ExtContentPolicy::TYPE_DOCUMENT:
    149    case ExtContentPolicy::TYPE_SUBDOCUMENT:
    150      MOZ_ALWAYS_SUCCEEDS(loadingContext->SetCurrentLoadIdentifier(
    151          Some(mLoadState->GetLoadIdentifier())));
    152      break;
    153 
    154    default:
    155      break;
    156  }
    157 
    158  mLoadState->AssertProcessCouldTriggerLoadIfSystem();
    159 
    160  DocumentChannelCreationArgs args(
    161      mozilla::WrapNotNull(mLoadState), TimeStamp::Now(), mChannelId, mCacheKey,
    162      mTiming, ipcClientInfo, ipcElementCreationArgs,
    163      loadingContext->GetParentInitiatedNavigationEpoch());
    164 
    165  gNeckoChild->SendPDocumentChannelConstructor(this, loadingContext, args);
    166 
    167  mIsPending = true;
    168  mWasOpened = true;
    169  mListener = listener;
    170 
    171  return NS_OK;
    172 }
    173 
    174 IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
    175    const nsresult& aStatusCode) {
    176  if (aStatusCode == NS_ERROR_RECURSIVE_DOCUMENT_LOAD) {
    177    // This exists so that we are able to fire an error event
    178    // for when there are too many recursive iframe or object loads.
    179    // This is an incomplete solution, because right now we don't have a unified
    180    // way of firing error events due to errors in document channel.
    181    // This should be fixed in bug 1629201.
    182    MOZ_DIAGNOSTIC_ASSERT(mLoadingContext);
    183    if (RefPtr<Element> embedder = mLoadingContext->GetEmbedderElement()) {
    184      if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedder)) {
    185        if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
    186          fl->FireErrorEvent();
    187        }
    188      }
    189    }
    190  }
    191  ShutdownListeners(aStatusCode);
    192  return IPC_OK();
    193 }
    194 
    195 IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
    196    const nsresult& aStatus, const nsresult& aLoadGroupStatus,
    197    bool aContinueNavigating) {
    198  // If this disconnect is not due to a process switch, perform the disconnect
    199  // immediately.
    200  if (!aContinueNavigating) {
    201    DisconnectChildListeners(aStatus, aLoadGroupStatus);
    202    return IPC_OK();
    203  }
    204 
    205  // Otherwise, the disconnect will occur later using some other mechanism,
    206  // depending on what's happening to the loading DocShell. If this is a
    207  // toplevel navigation, and this BrowsingContext enters the BFCache, we will
    208  // cancel this channel when the PageHide event is firing, whereas if it does
    209  // not enter BFCache (e.g. due to being an object, subframe or non-bfcached
    210  // toplevel navigation), we will cancel this channel when the DocShell is
    211  // destroyed.
    212  nsDocShell* shell = GetDocShell();
    213  if (mLoadInfo->GetExternalContentPolicyType() ==
    214          ExtContentPolicy::TYPE_DOCUMENT &&
    215      shell) {
    216    MOZ_ASSERT(shell->GetBrowsingContext()->IsTop());
    217    if (mozilla::SessionHistoryInParent() &&
    218        shell->GetBrowsingContext()->IsInBFCache()) {
    219      DisconnectChildListeners(aStatus, aLoadGroupStatus);
    220    } else {
    221      // Tell the DocShell which channel to cancel if it enters the BFCache.
    222      shell->SetChannelToDisconnectOnPageHide(mChannelId);
    223    }
    224  }
    225 
    226  return IPC_OK();
    227 }
    228 
    229 IPCResult DocumentChannelChild::RecvRedirectToRealChannel(
    230    RedirectToRealChannelArgs&& aArgs,
    231    nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
    232    RedirectToRealChannelResolver&& aResolve) {
    233  LOG(("DocumentChannelChild RecvRedirectToRealChannel [this=%p, uri=%s]", this,
    234       aArgs.uri()->GetSpecOrDefault().get()));
    235 
    236  // The document that created the cspToInherit.
    237  // This is used when deserializing LoadInfo from the parent
    238  // process, since we can't serialize Documents directly.
    239  // TODO: For a fission OOP iframe this will be unavailable,
    240  // as will the loadingContext computed in LoadInfoArgsToLoadInfo.
    241  // Figure out if we need these for cross-origin subdocs.
    242  RefPtr<dom::Document> cspToInheritLoadingDocument;
    243  nsCOMPtr<nsIContentSecurityPolicy> csp =
    244      PolicyContainer::GetCSP(mLoadState->PolicyContainer());
    245  if (csp) {
    246    nsWeakPtr ctx = nsCSPContext::Cast(csp.get())->GetLoadingContext();
    247    cspToInheritLoadingDocument = do_QueryReferent(ctx);
    248  }
    249  nsCOMPtr<nsILoadInfo> loadInfo;
    250  MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(aArgs.loadInfo(), NOT_REMOTE_TYPE,
    251                                             cspToInheritLoadingDocument,
    252                                             getter_AddRefs(loadInfo)));
    253 
    254  mRedirectResolver = std::move(aResolve);
    255 
    256  nsCOMPtr<nsIChannel> newChannel;
    257  MOZ_ASSERT((aArgs.loadStateInternalLoadFlags() &
    258              nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC) ||
    259             aArgs.srcdocData().IsVoid());
    260  nsresult rv = nsDocShell::CreateRealChannelForDocument(
    261      getter_AddRefs(newChannel), aArgs.uri(), loadInfo, nullptr,
    262      aArgs.newLoadFlags(), aArgs.srcdocData(), aArgs.baseUri());
    263  if (newChannel) {
    264    newChannel->SetLoadGroup(mLoadGroup);
    265  }
    266 
    267  if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
    268    httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
    269    httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
    270  }
    271 
    272  // This is used to report any errors back to the parent by calling
    273  // CrossProcessRedirectFinished.
    274  auto scopeExit = MakeScopeExit([&]() {
    275    mRedirectResolver(rv);
    276    mRedirectResolver = nullptr;
    277  });
    278 
    279  if (NS_FAILED(rv)) {
    280    return IPC_OK();
    281  }
    282 
    283  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel)) {
    284    rv = httpChannel->SetChannelId(aArgs.channelId());
    285    if (aArgs.referrerInfo()) {
    286      rv = httpChannel->SetReferrerInfo(aArgs.referrerInfo());
    287    }
    288  }
    289  if (NS_FAILED(rv)) {
    290    return IPC_OK();
    291  }
    292 
    293  rv = newChannel->SetOriginalURI(aArgs.originalURI());
    294  if (NS_FAILED(rv)) {
    295    return IPC_OK();
    296  }
    297 
    298  if (nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
    299          do_QueryInterface(newChannel)) {
    300    rv = httpChannelInternal->SetRedirectMode(aArgs.redirectMode());
    301  }
    302  if (NS_FAILED(rv)) {
    303    return IPC_OK();
    304  }
    305 
    306  newChannel->SetNotificationCallbacks(mCallbacks);
    307 
    308  if (aArgs.init()) {
    309    HttpBaseChannel::ReplacementChannelConfig config(*aArgs.init());
    310    HttpBaseChannel::ConfigureReplacementChannel(
    311        newChannel, config,
    312        HttpBaseChannel::ReplacementReason::DocumentChannel);
    313  }
    314 
    315  if (aArgs.contentDisposition()) {
    316    newChannel->SetContentDisposition(*aArgs.contentDisposition());
    317  }
    318 
    319  if (aArgs.contentDispositionFilename()) {
    320    newChannel->SetContentDispositionFilename(
    321        *aArgs.contentDispositionFilename());
    322  }
    323 
    324  nsDocShell* docShell = GetDocShell();
    325  if (docShell && aArgs.loadingSessionHistoryInfo().isSome()) {
    326    docShell->SetLoadingSessionHistoryInfo(
    327        aArgs.loadingSessionHistoryInfo().ref());
    328  }
    329 
    330  // transfer any properties. This appears to be entirely a content-side
    331  // interface and isn't copied across to the parent. Copying the values
    332  // for this from this into the new actor will work, since the parent
    333  // won't have the right details anyway.
    334  // TODO: What about the process switch equivalent
    335  // (ContentChild::RecvCrossProcessRedirect)? In that case there is no local
    336  // existing actor in the destination process... We really need all information
    337  // to go up to the parent, and then come down to the new child actor.
    338  if (nsCOMPtr<nsIWritablePropertyBag> bag = do_QueryInterface(newChannel)) {
    339    nsHashPropertyBag::CopyFrom(bag, aArgs.properties());
    340  }
    341 
    342  // connect parent.
    343  nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(newChannel);
    344  if (childChannel) {
    345    rv = childChannel->ConnectParent(
    346        aArgs.registrarId());  // creates parent channel
    347    if (NS_FAILED(rv)) {
    348      return IPC_OK();
    349    }
    350  }
    351  mRedirectChannel = newChannel;
    352  mStreamFilterEndpoints = std::move(aEndpoints);
    353 
    354  rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel,
    355                                            aArgs.redirectFlags(),
    356                                            GetMainThreadSerialEventTarget());
    357 
    358  if (NS_SUCCEEDED(rv)) {
    359    scopeExit.release();
    360  }
    361 
    362  // scopeExit will call CrossProcessRedirectFinished(rv) here
    363  return IPC_OK();
    364 }
    365 
    366 IPCResult DocumentChannelChild::RecvUpgradeObjectLoad(
    367    UpgradeObjectLoadResolver&& aResolve) {
    368  // We're doing a load for an <object> or <embed> element if we got here.
    369  MOZ_ASSERT(mLoadFlags & nsIRequest::LOAD_HTML_OBJECT_DATA,
    370             "Should have LOAD_HTML_OBJECT_DATA set");
    371  MOZ_ASSERT(!(mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI),
    372             "Shouldn't be a LOAD_DOCUMENT_URI load yet");
    373  MOZ_ASSERT(mLoadInfo->GetExternalContentPolicyType() ==
    374                 ExtContentPolicy::TYPE_OBJECT,
    375             "Should have the TYPE_OBJECT content policy type");
    376 
    377  // If our load has already failed, or been cancelled, abort this attempt to
    378  // upgade the load.
    379  if (NS_FAILED(mStatus)) {
    380    aResolve(nullptr);
    381    return IPC_OK();
    382  }
    383 
    384  nsCOMPtr<nsIObjectLoadingContent> loadingContent;
    385  NS_QueryNotificationCallbacks(this, loadingContent);
    386  if (!loadingContent) {
    387    return IPC_FAIL(this, "Channel is not for ObjectLoadingContent!");
    388  }
    389 
    390  // We're upgrading to a document channel now. Add the LOAD_DOCUMENT_URI flag
    391  // after-the-fact.
    392  mLoadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
    393 
    394  RefPtr<BrowsingContext> browsingContext;
    395  nsresult rv = loadingContent->UpgradeLoadToDocument(
    396      this, getter_AddRefs(browsingContext));
    397  if (NS_FAILED(rv) || !browsingContext) {
    398    // Oops! Looks like something went wrong, so let's bail out.
    399    mLoadFlags &= ~nsIChannel::LOAD_DOCUMENT_URI;
    400    aResolve(nullptr);
    401    return IPC_OK();
    402  }
    403 
    404  aResolve(browsingContext);
    405  return IPC_OK();
    406 }
    407 
    408 NS_IMETHODIMP
    409 DocumentChannelChild::OnRedirectVerifyCallback(nsresult aStatusCode) {
    410  LOG(
    411      ("DocumentChannelChild OnRedirectVerifyCallback [this=%p, "
    412       "aRv=0x%08" PRIx32 " ]",
    413       this, static_cast<uint32_t>(aStatusCode)));
    414  nsCOMPtr<nsIChannel> redirectChannel = std::move(mRedirectChannel);
    415  RedirectToRealChannelResolver redirectResolver = std::move(mRedirectResolver);
    416 
    417  // If we've already shut down, then just notify the parent that
    418  // we're done.
    419  if (NS_FAILED(mStatus)) {
    420    redirectChannel->SetNotificationCallbacks(nullptr);
    421    redirectResolver(aStatusCode);
    422    return NS_OK;
    423  }
    424 
    425  nsresult rv = aStatusCode;
    426  if (NS_SUCCEEDED(rv)) {
    427    if (nsCOMPtr<nsIChildChannel> childChannel =
    428            do_QueryInterface(redirectChannel)) {
    429      rv = childChannel->CompleteRedirectSetup(mListener);
    430    } else {
    431      rv = redirectChannel->AsyncOpen(mListener);
    432    }
    433  } else {
    434    redirectChannel->SetNotificationCallbacks(nullptr);
    435  }
    436 
    437  for (auto& endpoint : mStreamFilterEndpoints) {
    438    extensions::StreamFilterParent::Attach(redirectChannel,
    439                                           std::move(endpoint));
    440  }
    441 
    442  redirectResolver(rv);
    443 
    444  if (NS_FAILED(rv)) {
    445    ShutdownListeners(rv);
    446    return NS_OK;
    447  }
    448 
    449  if (mLoadGroup) {
    450    mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
    451  }
    452  mCallbacks = nullptr;
    453  mListener = nullptr;
    454 
    455  // This calls NeckoChild::DeallocPDocumentChannel(), which deletes |this| if
    456  // IPDL holds the last reference.  Don't rely on |this| existing after here!
    457  if (CanSend()) {
    458    Send__delete__(this);
    459  }
    460 
    461  return NS_OK;
    462 }
    463 
    464 NS_IMETHODIMP
    465 DocumentChannelChild::Cancel(nsresult aStatusCode) {
    466  return CancelWithReason(aStatusCode, "DocumentChannelChild::Cancel"_ns);
    467 }
    468 
    469 NS_IMETHODIMP DocumentChannelChild::CancelWithReason(
    470    nsresult aStatusCode, const nsACString& aReason) {
    471  if (mCanceled) {
    472    return NS_OK;
    473  }
    474 
    475  mCanceled = true;
    476  if (CanSend()) {
    477    SendCancel(aStatusCode, aReason);
    478  }
    479 
    480  ShutdownListeners(aStatusCode);
    481 
    482  return NS_OK;
    483 }
    484 
    485 }  // namespace net
    486 }  // namespace mozilla
    487 
    488 #undef LOG