tor-browser

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

FetchParent.cpp (14095B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "FetchParent.h"
      6 
      7 #include "FetchLog.h"
      8 #include "FetchService.h"
      9 #include "InternalRequest.h"
     10 #include "InternalResponse.h"
     11 #include "mozilla/dom/ClientInfo.h"
     12 #include "mozilla/dom/FetchTypes.h"
     13 #include "mozilla/dom/PerformanceTimingTypes.h"
     14 #include "mozilla/dom/ServiceWorkerDescriptor.h"
     15 #include "mozilla/ipc/BackgroundParent.h"
     16 #include "nsThreadUtils.h"
     17 
     18 using namespace mozilla::ipc;
     19 
     20 namespace mozilla::dom {
     21 
     22 NS_IMPL_ISUPPORTS(FetchParent::FetchParentCSPEventListener, nsICSPEventListener)
     23 
     24 FetchParent::FetchParentCSPEventListener::FetchParentCSPEventListener(
     25    const nsID& aActorID, nsCOMPtr<nsISerialEventTarget> aEventTarget)
     26    : mActorID(aActorID), mEventTarget(aEventTarget) {
     27  MOZ_ASSERT(mEventTarget);
     28  FETCH_LOG(("FetchParentCSPEventListener [%p] actor ID: %s", this,
     29             mActorID.ToString().get()));
     30 }
     31 
     32 NS_IMETHODIMP FetchParent::FetchParentCSPEventListener::OnCSPViolationEvent(
     33    const nsAString& aJSON) {
     34  AssertIsOnMainThread();
     35  FETCH_LOG(("FetchParentCSPEventListener::OnCSPViolationEvent [%p]", this));
     36 
     37  nsAutoString json(aJSON);
     38  nsCOMPtr<nsIRunnable> r =
     39      NS_NewRunnableFunction(__func__, [actorID = mActorID, json]() mutable {
     40        FETCH_LOG(
     41            ("FetchParentCSPEventListener::OnCSPViolationEvent, Runnale"));
     42        RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
     43        if (actor) {
     44          actor->OnCSPViolationEvent(json);
     45        }
     46      });
     47 
     48  MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
     49  return NS_OK;
     50 }
     51 
     52 MOZ_RUNINIT nsTHashMap<nsIDHashKey, RefPtr<FetchParent>>
     53    FetchParent::sActorTable;
     54 
     55 /*static*/
     56 RefPtr<FetchParent> FetchParent::GetActorByID(const nsID& aID) {
     57  AssertIsOnBackgroundThread();
     58  auto entry = sActorTable.Lookup(aID);
     59  if (entry) {
     60    return entry.Data();
     61  }
     62  return nullptr;
     63 }
     64 
     65 FetchParent::FetchParent() : mID(nsID::GenerateUUID()) {
     66  FETCH_LOG(("FetchParent::FetchParent [%p]", this));
     67  AssertIsOnBackgroundThread();
     68  mBackgroundEventTarget = GetCurrentSerialEventTarget();
     69  MOZ_ASSERT(mBackgroundEventTarget);
     70  if (!sActorTable.WithEntryHandle(mID, [&](auto&& entry) {
     71        if (entry.HasEntry()) {
     72          return false;
     73        }
     74        entry.Insert(this);
     75        return true;
     76      })) {
     77    FETCH_LOG(("FetchParent::FetchParent entry[%p] already exists", this));
     78  }
     79 }
     80 
     81 FetchParent::~FetchParent() {
     82  FETCH_LOG(("FetchParent::~FetchParent [%p]", this));
     83  // MOZ_ASSERT(!mBackgroundEventTarget);
     84  MOZ_ASSERT(mActorDestroyed && mIsDone);
     85  mResponsePromises = nullptr;
     86 }
     87 
     88 IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
     89  FETCH_LOG(("FetchParent::RecvFetchOp [%p]", this));
     90  AssertIsOnBackgroundThread();
     91 
     92  MOZ_ASSERT(!mIsDone);
     93  if (mActorDestroyed) {
     94    return IPC_OK();
     95  }
     96 
     97  mRequest = MakeSafeRefPtr<InternalRequest>(std::move(aArgs.request()));
     98  mIsWorkerFetch = aArgs.isWorkerRequest();
     99  mPrincipalInfo = std::move(aArgs.principalInfo());
    100  mWorkerScript = aArgs.workerScript();
    101  mClientInfo = Some(ClientInfo(aArgs.clientInfo()));
    102  if (aArgs.controller().isSome()) {
    103    mController = Some(ServiceWorkerDescriptor(aArgs.controller().ref()));
    104  }
    105  mCookieJarSettings = aArgs.cookieJarSettings();
    106  mNeedOnDataAvailable = aArgs.needOnDataAvailable();
    107  mHasCSPEventListener = aArgs.hasCSPEventListener();
    108  mIsThirdPartyContext = aArgs.isThirdPartyContext();
    109  mIsOn3PCBExceptionList = aArgs.isOn3PCBExceptionList();
    110 
    111  if (mHasCSPEventListener) {
    112    mCSPEventListener =
    113        MakeRefPtr<FetchParentCSPEventListener>(mID, mBackgroundEventTarget);
    114  }
    115  mAssociatedBrowsingContextID = aArgs.associatedBrowsingContextID();
    116 
    117  MOZ_ASSERT(!mPromise);
    118  mPromise = new GenericPromise::Private(__func__);
    119 
    120  RefPtr<FetchParent> self = this;
    121  mPromise->Then(
    122      mBackgroundEventTarget, __func__,
    123      [self](const bool&& result) mutable {
    124        FETCH_LOG(
    125            ("FetchParent::RecvFetchOp [%p] Success Callback", self.get()));
    126        AssertIsOnBackgroundThread();
    127        self->mPromise = nullptr;
    128        if (self->mIsDone) {
    129          FETCH_LOG(("FetchParent::RecvFetchOp [%p] Fetch has already aborted",
    130                     self.get()));
    131          if (!self->mActorDestroyed) {
    132            (void)NS_WARN_IF(
    133                !self->Send__delete__(self, NS_ERROR_DOM_ABORT_ERR));
    134          }
    135          return;
    136        }
    137        self->mIsDone = true;
    138        if (!self->mActorDestroyed && !self->mExtendForCSPEventListener) {
    139          FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(NS_OK)",
    140                     self.get()));
    141          (void)NS_WARN_IF(!self->Send__delete__(self, NS_OK));
    142        }
    143      },
    144      [self](const nsresult&& aErr) mutable {
    145        FETCH_LOG(
    146            ("FetchParent::RecvFetchOp [%p] Failure Callback", self.get()));
    147        AssertIsOnBackgroundThread();
    148        self->mIsDone = true;
    149        self->mPromise = nullptr;
    150        if (!self->mActorDestroyed) {
    151          FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(aErr)",
    152                     self.get()));
    153          (void)NS_WARN_IF(!self->Send__delete__(self, aErr));
    154        }
    155      });
    156 
    157  RefPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable {
    158    FETCH_LOG(
    159        ("FetchParent::RecvFetchOp [%p], Main Thread Runnable", self.get()));
    160    AssertIsOnMainThread();
    161    if (self->mIsDone) {
    162      MOZ_ASSERT(!self->mResponsePromises);
    163      MOZ_ASSERT(self->mPromise);
    164      FETCH_LOG(
    165          ("FetchParent::RecvFetchOp [%p], Main Thread Runnable, "
    166           "already aborted",
    167           self.get()));
    168      self->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
    169      return;
    170    }
    171    RefPtr<FetchService> fetchService = FetchService::GetInstance();
    172    MOZ_ASSERT(fetchService);
    173    MOZ_ASSERT(self->mRequest);
    174    MOZ_ASSERT(!self->mResponsePromises);
    175    if (self->mIsWorkerFetch) {
    176      self->mResponsePromises =
    177          fetchService->Fetch(AsVariant(FetchService::WorkerFetchArgs(
    178              {self->mRequest.clonePtr(), self->mPrincipalInfo,
    179               self->mWorkerScript, self->mClientInfo, self->mController,
    180               self->mCookieJarSettings, self->mNeedOnDataAvailable,
    181               self->mCSPEventListener, self->mAssociatedBrowsingContextID,
    182               self->mBackgroundEventTarget, self->mID,
    183               self->mIsThirdPartyContext,
    184               MozPromiseRequestHolder<FetchServiceResponseEndPromise>(),
    185               self->mPromise, self->mIsOn3PCBExceptionList})));
    186    } else {
    187      MOZ_ASSERT(self->mRequest->GetKeepalive());
    188      self->mResponsePromises =
    189          fetchService->Fetch(AsVariant(FetchService::MainThreadFetchArgs({
    190              self->mRequest.clonePtr(),
    191              self->mPrincipalInfo,
    192              self->mCookieJarSettings,
    193              self->mNeedOnDataAvailable,
    194              self->mCSPEventListener,
    195              self->mAssociatedBrowsingContextID,
    196              self->mBackgroundEventTarget,
    197              self->mID,
    198              self->mIsThirdPartyContext,
    199          })));
    200    }
    201 
    202    bool isResolved = self->mResponsePromises->IsResponseEndPromiseResolved();
    203    if (!isResolved && self->mIsWorkerFetch) {
    204      // track only unresolved promises for worker fetch requests
    205      // this is needed for clean-up of keepalive requests
    206      self->mResponsePromises->GetResponseEndPromise()
    207          ->Then(
    208              GetMainThreadSerialEventTarget(), __func__,
    209              [self](ResponseEndArgs&& aArgs) mutable {
    210                AssertIsOnMainThread();
    211                MOZ_ASSERT(self->mPromise);
    212                self->mPromise->Resolve(true, __func__);
    213                self->mResponsePromises = nullptr;
    214              },
    215              [self](CopyableErrorResult&& aErr) mutable {
    216                AssertIsOnMainThread();
    217                MOZ_ASSERT(self->mPromise);
    218                self->mPromise->Reject(aErr.StealNSResult(), __func__);
    219                self->mResponsePromises = nullptr;
    220              })
    221          ->Track(fetchService->GetResponseEndPromiseHolder(
    222              self->mResponsePromises));
    223    } else {
    224      self->mResponsePromises->GetResponseEndPromise()->Then(
    225          GetMainThreadSerialEventTarget(), __func__,
    226          [self](ResponseEndArgs&& aArgs) mutable {
    227            AssertIsOnMainThread();
    228            MOZ_ASSERT(self->mPromise);
    229            self->mPromise->Resolve(true, __func__);
    230            self->mResponsePromises = nullptr;
    231          },
    232          [self](CopyableErrorResult&& aErr) mutable {
    233            AssertIsOnMainThread();
    234            MOZ_ASSERT(self->mPromise);
    235            self->mPromise->Reject(aErr.StealNSResult(), __func__);
    236            self->mResponsePromises = nullptr;
    237          });
    238    }
    239  });
    240 
    241  MOZ_ALWAYS_SUCCEEDS(
    242      NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL));
    243 
    244  return IPC_OK();
    245 }
    246 
    247 IPCResult FetchParent::RecvAbortFetchOp(bool aForceAbort) {
    248  FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p]", this));
    249  AssertIsOnBackgroundThread();
    250 
    251  if (mIsDone) {
    252    FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p], Already aborted", this));
    253    return IPC_OK();
    254  }
    255 
    256  if (!aForceAbort && mRequest && mRequest->GetKeepalive()) {
    257    // Keeping FetchParent/FetchChild alive for the main-thread keepalive fetch
    258    // here is a temporary solution. The cancel logic should always be handled
    259    // in FetchInstance::Cancel() once all main-thread fetch routing through
    260    // PFetch.
    261    if (!mIsWorkerFetch) {
    262      FETCH_LOG(("Skip aborting fetch as the request is marked keepalive"));
    263      return IPC_OK();
    264    }
    265  } else {
    266    mIsDone = true;
    267  }
    268  RefPtr<FetchParent> self = this;
    269  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    270      __func__, [self, forceAbort = aForceAbort]() mutable {
    271        FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable"));
    272        AssertIsOnMainThread();
    273        if (self->mResponsePromises) {
    274          RefPtr<FetchService> fetchService = FetchService::GetInstance();
    275          MOZ_ASSERT(fetchService);
    276          fetchService->CancelFetch(std::move(self->mResponsePromises),
    277                                    forceAbort);
    278        }
    279      });
    280 
    281  MOZ_ALWAYS_SUCCEEDS(
    282      NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL));
    283 
    284  return IPC_OK();
    285 }
    286 
    287 void FetchParent::OnResponseAvailableInternal(
    288    SafeRefPtr<InternalResponse>&& aResponse) {
    289  FETCH_LOG(("FetchParent::OnResponseAvailableInternal [%p]", this));
    290  AssertIsOnBackgroundThread();
    291  MOZ_ASSERT(aResponse);
    292  MOZ_ASSERT(!mActorDestroyed);
    293 
    294  if (mIsDone && aResponse->Type() != ResponseType::Error) {
    295    FETCH_LOG(
    296        ("FetchParent::OnResponseAvailableInternal [%p] "
    297         "Fetch has already aborted",
    298         this));
    299    return;
    300  }
    301 
    302  // To monitor the stream status between processes, response's body can not
    303  // be serialized as RemoteLazyInputStream. Such that stream close can be
    304  // propagated to FetchDriver in the parent process.
    305  aResponse->SetSerializeAsLazy(false);
    306 
    307  // CSP violation notification is asynchronous. Extending the FetchParent's
    308  // life cycle for the notificaiton.
    309  if (aResponse->Type() == ResponseType::Error &&
    310      aResponse->GetErrorCode() == NS_ERROR_CONTENT_BLOCKED &&
    311      mCSPEventListener) {
    312    FETCH_LOG(
    313        ("FetchParent::OnResponseAvailableInternal [%p] "
    314         "NS_ERROR_CONTENT_BLOCKED",
    315         this));
    316    mExtendForCSPEventListener = true;
    317  }
    318 
    319  (void)SendOnResponseAvailableInternal(
    320      aResponse->ToParentToChildInternalResponse());
    321 }
    322 
    323 void FetchParent::OnResponseEnd(const ResponseEndArgs& aArgs) {
    324  FETCH_LOG(("FetchParent::OnResponseEnd [%p]", this));
    325  AssertIsOnBackgroundThread();
    326  MOZ_ASSERT(!mActorDestroyed);
    327 
    328  if (mIsDone && aArgs.endReason() != FetchDriverObserver::eAborted) {
    329    FETCH_LOG(
    330        ("FetchParent::OnResponseEnd [%p] "
    331         "Fetch has already aborted",
    332         this));
    333    return;
    334  }
    335 
    336  (void)SendOnResponseEnd(aArgs);
    337 }
    338 
    339 void FetchParent::OnDataAvailable() {
    340  FETCH_LOG(("FetchParent::OnDataAvailable [%p]", this));
    341  AssertIsOnBackgroundThread();
    342  MOZ_ASSERT(!mActorDestroyed);
    343 
    344  (void)SendOnDataAvailable();
    345 }
    346 
    347 void FetchParent::OnFlushConsoleReport(
    348    const nsTArray<net::ConsoleReportCollected>& aReports) {
    349  FETCH_LOG(("FetchParent::OnFlushConsoleReport [%p]", this));
    350  AssertIsOnBackgroundThread();
    351  MOZ_ASSERT(!mActorDestroyed);
    352 
    353  (void)SendOnFlushConsoleReport(aReports);
    354 }
    355 
    356 void FetchParent::OnReportPerformanceTiming(const ResponseTiming&& aTiming) {
    357  FETCH_LOG(("FetchParent::OnReportPerformanceTiming [%p]", this));
    358  AssertIsOnBackgroundThread();
    359  MOZ_ASSERT(!mActorDestroyed);
    360 
    361  (void)SendOnReportPerformanceTiming(aTiming);
    362 }
    363 
    364 void FetchParent::OnNotifyNetworkMonitorAlternateStack(uint64_t aChannelID) {
    365  FETCH_LOG(("FetchParent::OnNotifyNetworkMonitorAlternateStack [%p]", this));
    366  AssertIsOnBackgroundThread();
    367  MOZ_ASSERT(!mActorDestroyed);
    368 
    369  (void)SendOnNotifyNetworkMonitorAlternateStack(aChannelID);
    370 }
    371 
    372 void FetchParent::ActorDestroy(ActorDestroyReason aReason) {
    373  FETCH_LOG(("FetchParent::ActorDestroy [%p]", this));
    374  AssertIsOnBackgroundThread();
    375  mActorDestroyed = true;
    376  auto entry = sActorTable.Lookup(mID);
    377  if (entry) {
    378    entry.Remove();
    379    FETCH_LOG(("FetchParent::ActorDestroy entry [%p] removed", this));
    380  }
    381  // mRequest can be null when FetchParent has not yet received RecvFetchOp()
    382  if (!mRequest) {
    383    return;
    384  }
    385 
    386  // Abort the existing fetch.
    387  // Actor can be destoried by shutdown when still fetching.
    388  RecvAbortFetchOp(false);
    389 
    390  // mBackgroundEventTarget = nullptr;
    391 }
    392 
    393 nsICSPEventListener* FetchParent::GetCSPEventListener() {
    394  return mCSPEventListener;
    395 }
    396 
    397 void FetchParent::OnCSPViolationEvent(const nsAString& aJSON) {
    398  FETCH_LOG(("FetchParent::OnCSPViolationEvent [%p]", this));
    399  AssertIsOnBackgroundThread();
    400  MOZ_ASSERT(mHasCSPEventListener);
    401  MOZ_ASSERT(!mActorDestroyed);
    402 
    403  (void)SendOnCSPViolationEvent(aJSON);
    404 }
    405 
    406 }  // namespace mozilla::dom