tor-browser

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

FetchChild.cpp (16601B)


      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 "FetchChild.h"
      6 
      7 #include "FetchLog.h"
      8 #include "FetchObserver.h"
      9 #include "FetchUtil.h"
     10 #include "InternalResponse.h"
     11 #include "Request.h"
     12 #include "Response.h"
     13 #include "mozilla/ConsoleReportCollector.h"
     14 #include "mozilla/SchedulerGroup.h"
     15 #include "mozilla/dom/PerformanceStorage.h"
     16 #include "mozilla/dom/PerformanceTiming.h"
     17 #include "mozilla/dom/Promise.h"
     18 #include "mozilla/dom/RemoteWorkerChild.h"
     19 #include "mozilla/dom/SecurityPolicyViolationEventBinding.h"
     20 #include "mozilla/dom/WorkerChannelInfo.h"
     21 #include "mozilla/dom/WorkerPrivate.h"
     22 #include "mozilla/dom/WorkerRef.h"
     23 #include "mozilla/dom/WorkerScope.h"
     24 #include "nsIAsyncInputStream.h"
     25 #include "nsIGlobalObject.h"
     26 #include "nsIObserverService.h"
     27 #include "nsIRunnable.h"
     28 #include "nsIURI.h"
     29 #include "nsNetUtil.h"
     30 #include "nsThreadUtils.h"
     31 
     32 namespace mozilla::dom {
     33 
     34 NS_IMPL_ISUPPORTS0(FetchChild)
     35 
     36 mozilla::ipc::IPCResult FetchChild::Recv__delete__(const nsresult&& aResult) {
     37  FETCH_LOG(("FetchChild::Recv__delete__ [%p]", this));
     38  if (mIsShutdown) {
     39    return IPC_OK();
     40  }
     41  // Shutdown has not been called, so mWorkerRef->Private() should be still
     42  // alive.
     43  if (mWorkerRef) {
     44    MOZ_ASSERT(mWorkerRef->Private());
     45    mWorkerRef->Private()->AssertIsOnWorkerThread();
     46  } else {
     47    MOZ_ASSERT(mIsKeepAliveRequest);
     48  }
     49 
     50  if (mPromise->State() == Promise::PromiseState::Pending) {
     51    if (NS_FAILED(aResult)) {
     52      mPromise->MaybeReject(aResult);
     53      if (mFetchObserver) {
     54        mFetchObserver->SetState(FetchState::Errored);
     55      }
     56    } else {
     57      mPromise->MaybeResolve(aResult);
     58      if (mFetchObserver) {
     59        mFetchObserver->SetState(FetchState::Complete);
     60      }
     61    }
     62  }
     63  return IPC_OK();
     64 }
     65 
     66 mozilla::ipc::IPCResult FetchChild::RecvOnResponseAvailableInternal(
     67    ParentToChildInternalResponse&& aResponse) {
     68  FETCH_LOG(("FetchChild::RecvOnResponseAvailableInternal [%p]", this));
     69  if (mIsShutdown) {
     70    return IPC_OK();
     71  }
     72  // Shutdown has not been called, so mWorkerRef->Private() should be still
     73  // alive.
     74  if (mWorkerRef) {
     75    MOZ_ASSERT(mWorkerRef->Private());
     76    mWorkerRef->Private()->AssertIsOnWorkerThread();
     77  }
     78 
     79  SafeRefPtr<InternalResponse> internalResponse =
     80      InternalResponse::FromIPC(aResponse);
     81  IgnoredErrorResult result;
     82  internalResponse->Headers()->SetGuard(HeadersGuardEnum::Immutable, result);
     83  MOZ_ASSERT(internalResponse);
     84 
     85  if (internalResponse->Type() != ResponseType::Error) {
     86    if (internalResponse->Type() == ResponseType::Opaque) {
     87      internalResponse->GeneratePaddingInfo();
     88    }
     89 
     90    if (mFetchObserver) {
     91      mFetchObserver->SetState(FetchState::Complete);
     92    }
     93 
     94    // mFetchObserver->SetState runs JS and a blocking JS function can run
     95    // queued runnables, including ActorDestroy that nullifies mPromise.
     96    if (!mPromise) {
     97      return IPC_OK();
     98    }
     99    nsCOMPtr<nsIGlobalObject> global;
    100    global = mPromise->GetGlobalObject();
    101    RefPtr<Response> response =
    102        new Response(global, internalResponse.clonePtr(), mSignalImpl);
    103    mPromise->MaybeResolve(response);
    104 
    105    return IPC_OK();
    106  }
    107 
    108  FETCH_LOG(
    109      ("FetchChild::RecvOnResponseAvailableInternal [%p] response type is "
    110       "Error(0x%x)",
    111       this, static_cast<int32_t>(internalResponse->GetErrorCode())));
    112  if (mFetchObserver) {
    113    mFetchObserver->SetState(FetchState::Errored);
    114  }
    115 
    116  // mFetchObserver->SetState runs JS and a blocking JS function can run queued
    117  // runnables, including ActorDestroy that nullifies mPromise.
    118  if (!mPromise) {
    119    return IPC_OK();
    120  }
    121  mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
    122  return IPC_OK();
    123 }
    124 
    125 mozilla::ipc::IPCResult FetchChild::RecvOnResponseEnd(ResponseEndArgs&& aArgs) {
    126  FETCH_LOG(("FetchChild::RecvOnResponseEnd [%p]", this));
    127  if (mIsShutdown) {
    128    return IPC_OK();
    129  }
    130  // Shutdown has not been called, so mWorkerRef->Private() should be still
    131  // alive.
    132  if (mWorkerRef) {
    133    MOZ_ASSERT(mWorkerRef->Private());
    134    mWorkerRef->Private()->AssertIsOnWorkerThread();
    135  }
    136 
    137  if (aArgs.endReason() == FetchDriverObserver::eAborted) {
    138    FETCH_LOG(
    139        ("FetchChild::RecvOnResponseEnd [%p] endReason is eAborted", this));
    140    if (mFetchObserver) {
    141      mFetchObserver->SetState(FetchState::Errored);
    142    }
    143 
    144    // mFetchObserver->SetState runs JS and a blocking JS function can run
    145    // queued runnables, including ActorDestroy that nullifies mPromise.
    146    if (!mPromise) {
    147      return IPC_OK();
    148    }
    149    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
    150  }
    151 
    152  Unfollow();
    153  return IPC_OK();
    154 }
    155 
    156 mozilla::ipc::IPCResult FetchChild::RecvOnDataAvailable() {
    157  FETCH_LOG(("FetchChild::RecvOnDataAvailable [%p]", this));
    158  if (mIsShutdown) {
    159    return IPC_OK();
    160  }
    161  // Shutdown has not been called, so mWorkerRef->Private() should be still
    162  // alive.
    163  if (mWorkerRef) {
    164    MOZ_ASSERT(mWorkerRef->Private());
    165    mWorkerRef->Private()->AssertIsOnWorkerThread();
    166  }
    167 
    168  if (mFetchObserver && mFetchObserver->State() == FetchState::Requesting) {
    169    mFetchObserver->SetState(FetchState::Responding);
    170  }
    171  return IPC_OK();
    172 }
    173 
    174 mozilla::ipc::IPCResult FetchChild::RecvOnFlushConsoleReport(
    175    nsTArray<net::ConsoleReportCollected>&& aReports) {
    176  FETCH_LOG(("FetchChild::RecvOnFlushConsoleReport [%p]", this));
    177  if (mIsShutdown) {
    178    return IPC_OK();
    179  }
    180  MOZ_ASSERT(mReporter);
    181 
    182  if (NS_IsMainThread()) {
    183    MOZ_ASSERT(mIsKeepAliveRequest);
    184    // extract doc object to flush the console report
    185    for (const auto& report : aReports) {
    186      mReporter->AddConsoleReport(
    187          report.errorFlags(), report.category(),
    188          static_cast<nsContentUtils::PropertiesFile>(report.propertiesFile()),
    189          report.sourceFileURI(), report.lineNumber(), report.columnNumber(),
    190          report.messageName(), report.stringParams());
    191    }
    192 
    193    MOZ_ASSERT(mPromise);
    194    nsCOMPtr<nsPIDOMWindowInner> window =
    195        do_QueryInterface(mPromise->GetGlobalObject());
    196    if (window) {
    197      Document* doc = window->GetExtantDoc();
    198      mReporter->FlushConsoleReports(doc);
    199    } else {
    200      mReporter->FlushReportsToConsole(0);
    201    }
    202    return IPC_OK();
    203  }
    204  // Shutdown has not been called, so mWorkerRef->Private() should be still
    205  // alive.
    206  if (mWorkerRef) {
    207    MOZ_ASSERT(mWorkerRef->Private());
    208    mWorkerRef->Private()->AssertIsOnWorkerThread();
    209  }
    210 
    211  RefPtr<ThreadSafeWorkerRef> workerRef = mWorkerRef;
    212  nsCOMPtr<nsIConsoleReportCollector> reporter = mReporter;
    213 
    214  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    215      __func__, [reports = std::move(aReports), reporter = std::move(reporter),
    216                 workerRef = std::move(workerRef)]() mutable {
    217        for (const auto& report : reports) {
    218          reporter->AddConsoleReport(
    219              report.errorFlags(), report.category(),
    220              static_cast<nsContentUtils::PropertiesFile>(
    221                  report.propertiesFile()),
    222              report.sourceFileURI(), report.lineNumber(),
    223              report.columnNumber(), report.messageName(),
    224              report.stringParams());
    225        }
    226 
    227        if (workerRef->Private()->IsServiceWorker()) {
    228          reporter->FlushReportsToConsoleForServiceWorkerScope(
    229              workerRef->Private()->ServiceWorkerScope());
    230        }
    231 
    232        if (workerRef->Private()->IsSharedWorker()) {
    233          workerRef->Private()
    234              ->GetRemoteWorkerController()
    235              ->FlushReportsOnMainThread(reporter);
    236        }
    237 
    238        reporter->FlushConsoleReports(workerRef->Private()->GetLoadGroup());
    239      });
    240  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    241 
    242  return IPC_OK();
    243 }
    244 
    245 RefPtr<FetchChild> FetchChild::CreateForWorker(
    246    WorkerPrivate* aWorkerPrivate, RefPtr<Promise> aPromise,
    247    RefPtr<AbortSignalImpl> aSignalImpl, RefPtr<FetchObserver> aObserver) {
    248  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
    249  aWorkerPrivate->AssertIsOnWorkerThread();
    250  FETCH_LOG(("FetchChild::CreateForWorker [%p]", aWorkerPrivate));
    251 
    252  RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
    253      std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
    254 
    255  RefPtr<StrongWorkerRef> workerRef =
    256      StrongWorkerRef::Create(aWorkerPrivate, "FetchChild", [actor]() {
    257        FETCH_LOG(("StrongWorkerRef callback"));
    258        actor->Shutdown();
    259      });
    260  if (NS_WARN_IF(!workerRef)) {
    261    return nullptr;
    262  }
    263 
    264  actor->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
    265  if (NS_WARN_IF(!actor->mWorkerRef)) {
    266    return nullptr;
    267  }
    268  return actor;
    269 }
    270 
    271 RefPtr<FetchChild> FetchChild::CreateForMainThread(
    272    RefPtr<Promise> aPromise, RefPtr<AbortSignalImpl> aSignalImpl,
    273    RefPtr<FetchObserver> aObserver) {
    274  RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
    275      std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
    276  FETCH_LOG(("FetchChild::CreateForMainThread actor[%p]", actor.get()));
    277 
    278  return actor;
    279 }
    280 
    281 mozilla::ipc::IPCResult FetchChild::RecvOnCSPViolationEvent(
    282    const nsAString& aJSON) {
    283  FETCH_LOG(("FetchChild::RecvOnCSPViolationEvent [%p] aJSON: %s\n", this,
    284             NS_ConvertUTF16toUTF8(aJSON).BeginReading()));
    285 
    286  nsString JSON(aJSON);
    287 
    288  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [JSON]() mutable {
    289    SecurityPolicyViolationEventInit violationEventInit;
    290    if (NS_WARN_IF(!violationEventInit.Init(JSON))) {
    291      return;
    292    }
    293 
    294    nsCOMPtr<nsIURI> uri;
    295    nsresult rv =
    296        NS_NewURI(getter_AddRefs(uri), violationEventInit.mBlockedURI);
    297    if (NS_WARN_IF(NS_FAILED(rv))) {
    298      return;
    299    }
    300 
    301    nsCOMPtr<nsIObserverService> observerService =
    302        mozilla::services::GetObserverService();
    303    if (!observerService) {
    304      return;
    305    }
    306 
    307    rv = observerService->NotifyObservers(
    308        uri, CSP_VIOLATION_TOPIC, violationEventInit.mViolatedDirective.get());
    309    if (NS_WARN_IF(NS_FAILED(rv))) {
    310      return;
    311    }
    312  });
    313  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    314 
    315  if (mCSPEventListener) {
    316    (void)NS_WARN_IF(NS_FAILED(mCSPEventListener->OnCSPViolationEvent(aJSON)));
    317  }
    318  return IPC_OK();
    319 }
    320 
    321 mozilla::ipc::IPCResult FetchChild::RecvOnReportPerformanceTiming(
    322    ResponseTiming&& aTiming) {
    323  FETCH_LOG(("FetchChild::RecvOnReportPerformanceTiming [%p]", this));
    324  if (mIsShutdown) {
    325    return IPC_OK();
    326  }
    327  // Shutdown has not been called, so mWorkerRef->Private() should be still
    328  // alive.
    329  if (mWorkerRef) {
    330    MOZ_ASSERT(mWorkerRef->Private());
    331    mWorkerRef->Private()->AssertIsOnWorkerThread();
    332 
    333    RefPtr<PerformanceStorage> performanceStorage =
    334        mWorkerRef->Private()->GetPerformanceStorage();
    335    if (performanceStorage) {
    336      performanceStorage->AddEntry(
    337          aTiming.entryName(), aTiming.initiatorType(),
    338          MakeUnique<PerformanceTimingData>(aTiming.timingData()));
    339    }
    340  } else if (mIsKeepAliveRequest) {
    341    MOZ_ASSERT(mPromise->GetGlobalObject());
    342    auto* innerWindow = mPromise->GetGlobalObject()->GetAsInnerWindow();
    343    if (innerWindow) {
    344      mozilla::dom::Performance* performance = innerWindow->GetPerformance();
    345      if (performance) {
    346        performance->AsPerformanceStorage()->AddEntry(
    347            aTiming.entryName(), aTiming.initiatorType(),
    348            MakeUnique<PerformanceTimingData>(aTiming.timingData()));
    349      }
    350    }
    351  }
    352  return IPC_OK();
    353 }
    354 
    355 mozilla::ipc::IPCResult FetchChild::RecvOnNotifyNetworkMonitorAlternateStack(
    356    uint64_t aChannelID) {
    357  FETCH_LOG(
    358      ("FetchChild::RecvOnNotifyNetworkMonitorAlternateStack [%p]", this));
    359  if (mIsShutdown) {
    360    return IPC_OK();
    361  }
    362  // Shutdown has not been called, so mWorkerRef->Private() should be still
    363  // alive.
    364  if (mWorkerRef) {
    365    MOZ_ASSERT(mWorkerRef->Private());
    366    mWorkerRef->Private()->AssertIsOnWorkerThread();
    367 
    368    if (!mOriginStack) {
    369      return IPC_OK();
    370    }
    371 
    372    if (!mWorkerChannelInfo) {
    373      mWorkerChannelInfo = MakeRefPtr<WorkerChannelInfo>(
    374          aChannelID, mWorkerRef->Private()->AssociatedBrowsingContextID());
    375    }
    376 
    377    // Unfortunately, SerializedStackHolder can only be read on the main thread.
    378    // However, it doesn't block the fetch execution.
    379    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    380        __func__, [channel = mWorkerChannelInfo,
    381                   stack = std::move(mOriginStack)]() mutable {
    382          NotifyNetworkMonitorAlternateStack(channel, std::move(stack));
    383        });
    384 
    385    MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    386  } else {
    387    // Handle main-thread fetch requests
    388    if (!mOriginStack) {
    389      return IPC_OK();
    390    }
    391 
    392    if (!mWorkerChannelInfo) {
    393      // Get browsing context from the promise's global object
    394      uint64_t browsingContextID = 0;
    395      if (mPromise && mPromise->GetGlobalObject()) {
    396        if (auto* innerWindow =
    397                mPromise->GetGlobalObject()->GetAsInnerWindow()) {
    398          if (auto* browsingContext = innerWindow->GetBrowsingContext()) {
    399            browsingContextID = browsingContext->Id();
    400          }
    401        }
    402      }
    403      if (browsingContextID == 0) {
    404        FETCH_LOG(
    405            ("FetchChild::RecvOnNotifyNetworkMonitorAlternateStack: unable to "
    406             "get browsingContextID for main-thread fetch, channelID=%" PRIu64,
    407             aChannelID));
    408      }
    409      mWorkerChannelInfo =
    410          MakeRefPtr<WorkerChannelInfo>(aChannelID, browsingContextID);
    411    }
    412 
    413    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    414        __func__, [channel = mWorkerChannelInfo,
    415                   stack = std::move(mOriginStack)]() mutable {
    416          NotifyNetworkMonitorAlternateStack(channel, std::move(stack));
    417        });
    418 
    419    MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    420  }
    421 
    422  return IPC_OK();
    423 }
    424 
    425 void FetchChild::SetCSPEventListener(nsICSPEventListener* aListener) {
    426  MOZ_ASSERT(aListener && !mCSPEventListener);
    427  mCSPEventListener = aListener;
    428 }
    429 
    430 FetchChild::FetchChild(RefPtr<Promise>&& aPromise,
    431                       RefPtr<AbortSignalImpl>&& aSignalImpl,
    432                       RefPtr<FetchObserver>&& aObserver)
    433    : mPromise(std::move(aPromise)),
    434      mSignalImpl(std::move(aSignalImpl)),
    435      mFetchObserver(std::move(aObserver)),
    436      mReporter(new ConsoleReportCollector()) {
    437  FETCH_LOG(("FetchChild::FetchChild [%p]", this));
    438 }
    439 
    440 void FetchChild::RunAbortAlgorithm() {
    441  FETCH_LOG(("FetchChild::RunAbortAlgorithm [%p]", this));
    442  if (mIsShutdown) {
    443    return;
    444  }
    445  if (mWorkerRef || mIsKeepAliveRequest) {
    446    (void)SendAbortFetchOp(true);
    447  }
    448 }
    449 
    450 void FetchChild::DoFetchOp(const FetchOpArgs& aArgs) {
    451  FETCH_LOG(("FetchChild::DoFetchOp [%p]", this));
    452  // we need to store this for keepalive request
    453  // as we need to update the load group during actor termination
    454  mIsKeepAliveRequest = aArgs.request().keepalive();
    455  if (mIsKeepAliveRequest) {
    456    mKeepaliveRequestSize =
    457        aArgs.request().bodySize() > 0 ? aArgs.request().bodySize() : 0;
    458  }
    459  if (mSignalImpl) {
    460    if (mSignalImpl->Aborted()) {
    461      (void)SendAbortFetchOp(true);
    462      return;
    463    }
    464    Follow(mSignalImpl);
    465  }
    466  (void)SendFetchOp(aArgs);
    467 }
    468 
    469 void FetchChild::Shutdown() {
    470  // This is invoked for worker fetch requests only.
    471  // We need to modify this to be invoked for main-thread fetch requests as
    472  // well. Typically during global teardown. See Bug 1901082
    473 
    474  FETCH_LOG(("FetchChild::Shutdown [%p]", this));
    475  if (mIsShutdown) {
    476    return;
    477  }
    478  mIsShutdown.Flip();
    479 
    480  // If mWorkerRef is nullptr here, that means Recv__delete__() must be called
    481  if (!mWorkerRef) {
    482    return;
    483  }
    484  mPromise = nullptr;
    485  mFetchObserver = nullptr;
    486  Unfollow();
    487  mSignalImpl = nullptr;
    488  mCSPEventListener = nullptr;
    489  SendAbortFetchOp(false);
    490 
    491  mWorkerRef = nullptr;
    492 }
    493 
    494 void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
    495  FETCH_LOG(("FetchChild::ActorDestroy [%p]", this));
    496  // for keepalive request decrement the pending keepalive count
    497  if (mIsKeepAliveRequest) {
    498    // For workers we do not have limit per load group rather we have limit
    499    // per request
    500    if (NS_IsMainThread()) {
    501      MOZ_ASSERT(mPromise->GetGlobalObject());
    502      nsCOMPtr<nsILoadGroup> loadGroup =
    503          FetchUtil::GetLoadGroupFromGlobal(mPromise->GetGlobalObject());
    504      if (loadGroup) {
    505        FetchUtil::DecrementPendingKeepaliveRequestSize(loadGroup,
    506                                                        mKeepaliveRequestSize);
    507      }
    508    }
    509  }
    510  mPromise = nullptr;
    511  mFetchObserver = nullptr;
    512  mSignalImpl = nullptr;
    513  mCSPEventListener = nullptr;
    514  mWorkerRef = nullptr;
    515 }
    516 
    517 }  // namespace mozilla::dom