tor-browser

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

GIOChannelChild.cpp (14400B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=4 sw=2 sts=2 et 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 "mozilla/net/NeckoChild.h"
      8 #include "GIOChannelChild.h"
      9 #include "nsGIOProtocolHandler.h"
     10 #include "mozilla/dom/ContentChild.h"
     11 #include "mozilla/dom/BrowserChild.h"
     12 #include "nsContentUtils.h"
     13 #include "nsIBrowserChild.h"
     14 #include "nsStringStream.h"
     15 #include "nsNetUtil.h"
     16 #include "mozilla/ipc/IPCStreamUtils.h"
     17 #include "mozilla/ipc/URIUtils.h"
     18 #include "SerializedLoadContext.h"
     19 #include "mozilla/ipc/BackgroundUtils.h"
     20 #include "nsIURIMutator.h"
     21 #include "nsContentSecurityManager.h"
     22 #include "SerializedLoadContext.h"
     23 #include "mozilla/Logging.h"
     24 
     25 using mozilla::dom::ContentChild;
     26 
     27 namespace mozilla {
     28 #undef LOG
     29 #define LOG(args) MOZ_LOG(gGIOLog, mozilla::LogLevel::Debug, args)
     30 namespace net {
     31 
     32 GIOChannelChild::GIOChannelChild(nsIURI* aUri)
     33    : mEventQ(new ChannelEventQueue(static_cast<nsIChildChannel*>(this))) {
     34  SetURI(aUri);
     35  // We could support thread retargeting, but as long as we're being driven by
     36  // IPDL on the main thread it doesn't buy us anything.
     37  DisallowThreadRetargeting();
     38 }
     39 
     40 void GIOChannelChild::AddIPDLReference() {
     41  MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
     42  mIPCOpen = true;
     43  AddRef();
     44 }
     45 
     46 void GIOChannelChild::ReleaseIPDLReference() {
     47  MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
     48  mIPCOpen = false;
     49  Release();
     50 }
     51 
     52 //-----------------------------------------------------------------------------
     53 // GIOChannelChild::nsISupports
     54 //-----------------------------------------------------------------------------
     55 
     56 NS_IMPL_ISUPPORTS_INHERITED(GIOChannelChild, nsBaseChannel, nsIChildChannel)
     57 
     58 //-----------------------------------------------------------------------------
     59 
     60 NS_IMETHODIMP
     61 GIOChannelChild::AsyncOpen(nsIStreamListener* aListener) {
     62  nsCOMPtr<nsIStreamListener> listener = aListener;
     63  nsresult rv =
     64      nsContentSecurityManager::doContentSecurityCheck(this, listener);
     65  NS_ENSURE_SUCCESS(rv, rv);
     66 
     67  LOG(("GIOChannelChild::AsyncOpen [this=%p]\n", this));
     68 
     69  NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
     70  NS_ENSURE_TRUE(
     71      !static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown(),
     72      NS_ERROR_FAILURE);
     73  NS_ENSURE_ARG_POINTER(listener);
     74  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     75  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
     76 
     77  // Port checked in parent, but duplicate here so we can return with error
     78  // immediately, as we've done since before e10s.
     79  rv = NS_CheckPortSafety(nsBaseChannel::URI());  // Need to disambiguate,
     80                                                  // because in the child ipdl,
     81                                                  // a typedef URI is defined...
     82  if (NS_FAILED(rv)) {
     83    return rv;
     84  }
     85 
     86  mozilla::dom::BrowserChild* browserChild = nullptr;
     87  nsCOMPtr<nsIBrowserChild> iBrowserChild;
     88  NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
     89                                NS_GET_IID(nsIBrowserChild),
     90                                getter_AddRefs(iBrowserChild));
     91  GetCallback(iBrowserChild);
     92  if (iBrowserChild) {
     93    browserChild =
     94        static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
     95  }
     96 
     97  mListener = listener;
     98 
     99  // add ourselves to the load group.
    100  if (mLoadGroup) {
    101    mLoadGroup->AddRequest(this, nullptr);
    102  }
    103 
    104  Maybe<mozilla::ipc::IPCStream> ipcStream;
    105  mozilla::ipc::SerializeIPCStream(do_AddRef(mUploadStream), ipcStream,
    106                                   /* aAllowLazy */ false);
    107 
    108  uint32_t loadFlags = 0;
    109  GetLoadFlags(&loadFlags);
    110 
    111  GIOChannelOpenArgs openArgs;
    112  SerializeURI(nsBaseChannel::URI(), openArgs.uri());
    113  openArgs.startPos() = mStartPos;
    114  openArgs.entityID() = mEntityID;
    115  openArgs.uploadStream() = ipcStream;
    116  openArgs.loadFlags() = loadFlags;
    117 
    118  nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
    119  rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
    120  NS_ENSURE_SUCCESS(rv, rv);
    121 
    122  // This must happen before the constructor message is sent.
    123  SetupNeckoTarget();
    124 
    125  // The socket transport layer in the chrome process now has a logical ref to
    126  // us until OnStopRequest is called.
    127  AddIPDLReference();
    128 
    129  if (!gNeckoChild->SendPGIOChannelConstructor(
    130          this, browserChild, IPC::SerializedLoadContext(this), openArgs)) {
    131    return NS_ERROR_FAILURE;
    132  }
    133 
    134  mIsPending = true;
    135  mWasOpened = true;
    136 
    137  return rv;
    138 }
    139 
    140 NS_IMETHODIMP
    141 GIOChannelChild::IsPending(bool* aResult) {
    142  *aResult = mIsPending;
    143  return NS_OK;
    144 }
    145 
    146 nsresult GIOChannelChild::OpenContentStream(bool aAsync,
    147                                            nsIInputStream** aStream,
    148                                            nsIChannel** aChannel) {
    149  MOZ_CRASH("GIOChannel*Child* should never have OpenContentStream called!");
    150  return NS_OK;
    151 }
    152 
    153 mozilla::ipc::IPCResult GIOChannelChild::RecvOnStartRequest(
    154    const nsresult& aChannelStatus, const int64_t& aContentLength,
    155    const nsACString& aContentType, const nsACString& aEntityID,
    156    const URIParams& aURI) {
    157  LOG(("GIOChannelChild::RecvOnStartRequest [this=%p]\n", this));
    158  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    159      this, [self = UnsafePtr<GIOChannelChild>(this), aChannelStatus,
    160             aContentLength, aContentType = nsCString(aContentType),
    161             aEntityID = nsCString(aEntityID), aURI]() {
    162        self->DoOnStartRequest(aChannelStatus, aContentLength, aContentType,
    163                               aEntityID, aURI);
    164      }));
    165  return IPC_OK();
    166 }
    167 
    168 void GIOChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
    169                                       const int64_t& aContentLength,
    170                                       const nsACString& aContentType,
    171                                       const nsACString& aEntityID,
    172                                       const URIParams& aURI) {
    173  LOG(("GIOChannelChild::DoOnStartRequest [this=%p]\n", this));
    174  if (!mCanceled && NS_SUCCEEDED(mStatus)) {
    175    mStatus = aChannelStatus;
    176  }
    177 
    178  mContentLength = aContentLength;
    179  SetContentType(aContentType);
    180  mEntityID = aEntityID;
    181 
    182  nsCString spec;
    183  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
    184  nsresult rv = uri->GetSpec(spec);
    185  if (NS_SUCCEEDED(rv)) {
    186    // Changes nsBaseChannel::URI()
    187    rv = NS_MutateURI(mURI).SetSpec(spec).Finalize(mURI);
    188  }
    189 
    190  if (NS_FAILED(rv)) {
    191    Cancel(rv);
    192  }
    193 
    194  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    195  rv = mListener->OnStartRequest(this);
    196  if (NS_FAILED(rv)) {
    197    Cancel(rv);
    198  }
    199 }
    200 
    201 mozilla::ipc::IPCResult GIOChannelChild::RecvOnDataAvailable(
    202    const nsresult& aChannelStatus, const nsACString& aData,
    203    const uint64_t& aOffset, const uint32_t& aCount) {
    204  LOG(("GIOChannelChild::RecvOnDataAvailable [this=%p]\n", this));
    205  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    206      this, [self = UnsafePtr<GIOChannelChild>(this), aChannelStatus,
    207             aData = nsCString(aData), aOffset, aCount]() {
    208        self->DoOnDataAvailable(aChannelStatus, aData, aOffset, aCount);
    209      }));
    210 
    211  return IPC_OK();
    212 }
    213 
    214 void GIOChannelChild::DoOnDataAvailable(const nsresult& aChannelStatus,
    215                                        const nsACString& aData,
    216                                        const uint64_t& aOffset,
    217                                        const uint32_t& aCount) {
    218  LOG(("GIOChannelChild::DoOnDataAvailable [this=%p]\n", this));
    219 
    220  if (!mCanceled && NS_SUCCEEDED(mStatus)) {
    221    mStatus = aChannelStatus;
    222  }
    223 
    224  if (mCanceled) {
    225    return;
    226  }
    227 
    228  // NOTE: the OnDataAvailable contract requires the client to read all the data
    229  // in the inputstream.  This code relies on that ('data' will go away after
    230  // this function).  Apparently the previous, non-e10s behavior was to actually
    231  // support only reading part of the data, allowing later calls to read the
    232  // rest.
    233  nsCOMPtr<nsIInputStream> stringStream;
    234  nsresult rv =
    235      NS_NewByteInputStream(getter_AddRefs(stringStream),
    236                            Span(aData).To(aCount), NS_ASSIGNMENT_DEPEND);
    237  if (NS_FAILED(rv)) {
    238    Cancel(rv);
    239    return;
    240  }
    241 
    242  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    243  rv = mListener->OnDataAvailable(this, stringStream, aOffset, aCount);
    244  if (NS_FAILED(rv)) {
    245    Cancel(rv);
    246  }
    247  stringStream->Close();
    248 }
    249 
    250 mozilla::ipc::IPCResult GIOChannelChild::RecvOnStopRequest(
    251    const nsresult& aChannelStatus) {
    252  LOG(("GIOChannelChild::RecvOnStopRequest [this=%p status=%" PRIx32 "]\n",
    253       this, static_cast<uint32_t>(aChannelStatus)));
    254 
    255  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    256      this, [self = UnsafePtr<GIOChannelChild>(this), aChannelStatus]() {
    257        self->DoOnStopRequest(aChannelStatus);
    258      }));
    259  return IPC_OK();
    260 }
    261 
    262 void GIOChannelChild::DoOnStopRequest(const nsresult& aChannelStatus) {
    263  LOG(("GIOChannelChild::DoOnStopRequest [this=%p status=%" PRIx32 "]\n", this,
    264       static_cast<uint32_t>(aChannelStatus)));
    265 
    266  if (!mCanceled) {
    267    mStatus = aChannelStatus;
    268  }
    269 
    270  {  // Ensure that all queued ipdl events are dispatched before
    271    // we initiate protocol deletion below.
    272    mIsPending = false;
    273    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    274    (void)mListener->OnStopRequest(this, aChannelStatus);
    275 
    276    mListener = nullptr;
    277 
    278    if (mLoadGroup) {
    279      mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
    280    }
    281  }
    282 
    283  // This calls NeckoChild::DeallocPGIOChannelChild(), which deletes |this| if
    284  // IPDL holds the last reference.  Don't rely on |this| existing after here!
    285  Send__delete__(this);
    286 }
    287 
    288 mozilla::ipc::IPCResult GIOChannelChild::RecvFailedAsyncOpen(
    289    const nsresult& aStatusCode) {
    290  LOG(("GIOChannelChild::RecvFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
    291       this, static_cast<uint32_t>(aStatusCode)));
    292  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    293      this, [self = UnsafePtr<GIOChannelChild>(this), aStatusCode]() {
    294        self->DoFailedAsyncOpen(aStatusCode);
    295      }));
    296  return IPC_OK();
    297 }
    298 
    299 void GIOChannelChild::DoFailedAsyncOpen(const nsresult& aStatusCode) {
    300  LOG(("GIOChannelChild::DoFailedAsyncOpen [this=%p status=%" PRIx32 "]\n",
    301       this, static_cast<uint32_t>(aStatusCode)));
    302  mStatus = aStatusCode;
    303 
    304  if (mLoadGroup) {
    305    mLoadGroup->RemoveRequest(this, nullptr, aStatusCode);
    306  }
    307 
    308  if (mListener) {
    309    mListener->OnStartRequest(this);
    310    mIsPending = false;
    311    mListener->OnStopRequest(this, aStatusCode);
    312  } else {
    313    mIsPending = false;
    314  }
    315 
    316  mListener = nullptr;
    317 
    318  if (mIPCOpen) {
    319    Send__delete__(this);
    320  }
    321 }
    322 
    323 mozilla::ipc::IPCResult GIOChannelChild::RecvDeleteSelf() {
    324  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    325      this,
    326      [self = UnsafePtr<GIOChannelChild>(this)]() { self->DoDeleteSelf(); }));
    327  return IPC_OK();
    328 }
    329 
    330 void GIOChannelChild::DoDeleteSelf() {
    331  if (mIPCOpen) {
    332    Send__delete__(this);
    333  }
    334 }
    335 
    336 //-----------------------------------------------------------------------------
    337 // GIOChannelChild::nsIResumableChannel
    338 //-----------------------------------------------------------------------------
    339 
    340 NS_IMETHODIMP
    341 GIOChannelChild::Cancel(nsresult aStatus) {
    342  LOG(("GIOChannelChild::Cancel [this=%p]\n", this));
    343 
    344  if (mCanceled) {
    345    return NS_OK;
    346  }
    347 
    348  mCanceled = true;
    349  mStatus = aStatus;
    350  if (mIPCOpen) {
    351    SendCancel(aStatus);
    352  }
    353  return NS_OK;
    354 }
    355 
    356 NS_IMETHODIMP
    357 GIOChannelChild::Suspend() {
    358  NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
    359 
    360  LOG(("GIOChannelChild::Suspend [this=%p]\n", this));
    361 
    362  // SendSuspend only once, when suspend goes from 0 to 1.
    363  if (!mSuspendCount++) {
    364    SendSuspend();
    365    mSuspendSent = true;
    366  }
    367  mEventQ->Suspend();
    368 
    369  return NS_OK;
    370 }
    371 
    372 NS_IMETHODIMP
    373 GIOChannelChild::Resume() {
    374  NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
    375 
    376  LOG(("GIOChannelChild::Resume [this=%p]\n", this));
    377 
    378  // SendResume only once, when suspend count drops to 0.
    379  if (!--mSuspendCount && mSuspendSent) {
    380    SendResume();
    381  }
    382  mEventQ->Resume();
    383 
    384  return NS_OK;
    385 }
    386 
    387 //-----------------------------------------------------------------------------
    388 // GIOChannelChild::nsIChildChannel
    389 //-----------------------------------------------------------------------------
    390 
    391 NS_IMETHODIMP
    392 GIOChannelChild::ConnectParent(uint32_t aId) {
    393  NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
    394  NS_ENSURE_TRUE(
    395      !static_cast<ContentChild*>(gNeckoChild->Manager())->IsShuttingDown(),
    396      NS_ERROR_FAILURE);
    397 
    398  LOG(("GIOChannelChild::ConnectParent [this=%p]\n", this));
    399 
    400  mozilla::dom::BrowserChild* browserChild = nullptr;
    401  nsCOMPtr<nsIBrowserChild> iBrowserChild;
    402  NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
    403                                NS_GET_IID(nsIBrowserChild),
    404                                getter_AddRefs(iBrowserChild));
    405  GetCallback(iBrowserChild);
    406  if (iBrowserChild) {
    407    browserChild =
    408        static_cast<mozilla::dom::BrowserChild*>(iBrowserChild.get());
    409  }
    410 
    411  // This must happen before the constructor message is sent.
    412  SetupNeckoTarget();
    413 
    414  // The socket transport in the chrome process now holds a logical ref to us
    415  // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
    416  AddIPDLReference();
    417 
    418  GIOChannelConnectArgs connectArgs(aId);
    419 
    420  if (!gNeckoChild->SendPGIOChannelConstructor(
    421          this, browserChild, IPC::SerializedLoadContext(this), connectArgs)) {
    422    return NS_ERROR_FAILURE;
    423  }
    424 
    425  return NS_OK;
    426 }
    427 
    428 NS_IMETHODIMP
    429 GIOChannelChild::CompleteRedirectSetup(nsIStreamListener* aListener) {
    430  LOG(("GIOChannelChild::CompleteRedirectSetup [this=%p]\n", this));
    431  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
    432  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
    433 
    434  mIsPending = true;
    435  mWasOpened = true;
    436  mListener = aListener;
    437 
    438  // add ourselves to the load group.
    439  if (mLoadGroup) {
    440    mLoadGroup->AddRequest(this, nullptr);
    441  }
    442 
    443  // We already have an open IPDL connection to the parent. If on-modify-request
    444  // listeners or load group observers canceled us, let the parent handle it
    445  // and send it back to us naturally.
    446  return NS_OK;
    447 }
    448 
    449 void GIOChannelChild::SetupNeckoTarget() {
    450  if (mNeckoTarget) {
    451    return;
    452  }
    453  mNeckoTarget = GetMainThreadSerialEventTarget();
    454 }
    455 
    456 }  // namespace net
    457 }  // namespace mozilla