Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/fetch/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 30 kB image not shown  

Quelle  FetchService.cpp   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "FetchLog.h"
#include "FetchParent.h"
#include "nsContentUtils.h"
#include "nsIContentSecurityPolicy.h"
#include "nsICookieJarSettings.h"
#include "nsILoadGroup.h"
#include "nsILoadInfo.h"
#include "nsIIOService.h"
#include "nsIObserverService.h"
#include "nsIPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/FetchService.h"
#include "mozilla/dom/InternalRequest.h"
#include "mozilla/dom/InternalResponse.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h"
#include "mozilla/glean/NetwerkMetrics.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/net/CookieJarSettings.h"

namespace mozilla::dom {

mozilla::LazyLogModule gFetchLog("Fetch");

// FetchServicePromises

FetchServicePromises::FetchServicePromises()
    : mAvailablePromise(
          MakeRefPtr<FetchServiceResponseAvailablePromise::Private>(__func__)),
      mTimingPromise(
          MakeRefPtr<FetchServiceResponseTimingPromise::Private>(__func__)),
      mEndPromise(
          MakeRefPtr<FetchServiceResponseEndPromise::Private>(__func__)) {
  mAvailablePromise->UseSynchronousTaskDispatch(__func__);
  mTimingPromise->UseSynchronousTaskDispatch(__func__);
  mEndPromise->UseSynchronousTaskDispatch(__func__);
}

RefPtr<FetchServiceResponseAvailablePromise>
FetchServicePromises::GetResponseAvailablePromise() {
  return mAvailablePromise;
}

RefPtr<FetchServiceResponseTimingPromise>
FetchServicePromises::GetResponseTimingPromise() {
  return mTimingPromise;
}

RefPtr<FetchServiceResponseEndPromise>
FetchServicePromises::GetResponseEndPromise() {
  return mEndPromise;
}

void FetchServicePromises::ResolveResponseAvailablePromise(
    FetchServiceResponse&& aResponse, StaticString aMethodName) {
  if (mAvailablePromise) {
    mAvailablePromiseResolved = true;
    mAvailablePromise->Resolve(std::move(aResponse), aMethodName);
  }
}

void FetchServicePromises::RejectResponseAvailablePromise(
    const CopyableErrorResult&& aError, StaticString aMethodName) {
  if (mAvailablePromise) {
    mAvailablePromise->Reject(aError, aMethodName);
  }
}

void FetchServicePromises::ResolveResponseTimingPromise(
    ResponseTiming&& aTiming, StaticString aMethodName) {
  if (mTimingPromise) {
    mTimingPromiseResolved = true;
    mTimingPromise->Resolve(std::move(aTiming), aMethodName);
  }
}

void FetchServicePromises::RejectResponseTimingPromise(
    const CopyableErrorResult&& aError, StaticString aMethodName) {
  if (mTimingPromise) {
    mTimingPromise->Reject(aError, aMethodName);
  }
}

void FetchServicePromises::ResolveResponseEndPromise(ResponseEndArgs&& aArgs,
                                                     StaticString aMethodName) {
  if (mEndPromise) {
    mEndPromiseResolved = true;
    mEndPromise->Resolve(std::move(aArgs), aMethodName);
  }
}

void FetchServicePromises::RejectResponseEndPromise(
    const CopyableErrorResult&& aError, StaticString aMethodName) {
  if (mEndPromise) {
    mEndPromise->Reject(aError, aMethodName);
  }
}

// FetchInstance

nsresult FetchService::FetchInstance::Initialize(FetchArgs&& aArgs) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!aArgs.is<UnknownArgs>() && mArgs.is<UnknownArgs>());

  mArgs = std::move(aArgs);

  // Get needed information for FetchDriver from passed-in channel.
  if (mArgs.is<NavigationPreloadArgs>()) {
    mRequest = mArgs.as<NavigationPreloadArgs>().mRequest.clonePtr();
    mArgsType = FetchArgsType::NavigationPreload;
    nsIChannel* channel = mArgs.as<NavigationPreloadArgs>().mChannel;
    FETCH_LOG(("FetchInstance::Initialize [%p] request[%p], channel[%p]"this,
               mRequest.unsafeGetRawPtr(), channel));

    nsresult rv;
    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    MOZ_ASSERT(loadInfo);

    nsCOMPtr<nsIURI> channelURI;
    rv = channel->GetURI(getter_AddRefs(channelURI));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    nsIScriptSecurityManager* securityManager =
        nsContentUtils::GetSecurityManager();
    if (securityManager) {
      securityManager->GetChannelResultPrincipal(channel,
                                                 getter_AddRefs(mPrincipal));
    }

    if (!mPrincipal) {
      return NS_ERROR_UNEXPECTED;
    }

    // Get loadGroup from channel
    rv = channel->GetLoadGroup(getter_AddRefs(mLoadGroup));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
    if (!mLoadGroup) {
      rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), mPrincipal);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
    }

    // Get CookieJarSettings from channel
    rv = loadInfo->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    // Get PerformanceStorage from channel
    mPerformanceStorage = loadInfo->GetPerformanceStorage();
  } else if (mArgs.is<MainThreadFetchArgs>()) {
    mArgsType = FetchArgsType::MainThreadFetch;

    mRequest = mArgs.as<MainThreadFetchArgs>().mRequest.clonePtr();

    FETCH_LOG(("FetchInstance::Initialize [%p] request[%p]"this,
               mRequest.unsafeGetRawPtr()));

    auto principalOrErr = PrincipalInfoToPrincipal(
        mArgs.as<MainThreadFetchArgs>().mPrincipalInfo);
    if (principalOrErr.isErr()) {
      return principalOrErr.unwrapErr();
    }
    mPrincipal = principalOrErr.unwrap();
    nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), mPrincipal);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    if (mArgs.as<MainThreadFetchArgs>().mCookieJarSettings.isSome()) {
      net::CookieJarSettings::Deserialize(
          mArgs.as<MainThreadFetchArgs>().mCookieJarSettings.ref(),
          getter_AddRefs(mCookieJarSettings));
    }

    return NS_OK;

  } else {
    mRequest = mArgs.as<WorkerFetchArgs>().mRequest.clonePtr();
    mArgsType = FetchArgsType::WorkerFetch;

    FETCH_LOG(("FetchInstance::Initialize [%p] request[%p]"this,
               mRequest.unsafeGetRawPtr()));

    auto principalOrErr =
        PrincipalInfoToPrincipal(mArgs.as<WorkerFetchArgs>().mPrincipalInfo);
    if (principalOrErr.isErr()) {
      return principalOrErr.unwrapErr();
    }
    mPrincipal = principalOrErr.unwrap();
    nsresult rv = NS_NewLoadGroup(getter_AddRefs(mLoadGroup), mPrincipal);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    if (mArgs.as<WorkerFetchArgs>().mCookieJarSettings.isSome()) {
      net::CookieJarSettings::Deserialize(
          mArgs.as<WorkerFetchArgs>().mCookieJarSettings.ref(),
          getter_AddRefs(mCookieJarSettings));
    }
  }

  return NS_OK;
}

RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  MOZ_ASSERT(mPrincipal);
  MOZ_ASSERT(mLoadGroup);

  nsAutoCString principalSpec;
  MOZ_ALWAYS_SUCCEEDS(mPrincipal->GetAsciiSpec(principalSpec));
  nsAutoCString requestURL;
  mRequest->GetURL(requestURL);
  FETCH_LOG(("FetchInstance::Fetch [%p], mRequest URL: %s mPrincipal: %s"this,
             requestURL.BeginReading(), principalSpec.BeginReading()));

  nsresult rv;

  if (mRequest->GetKeepalive()) {
    nsAutoCString origin;
    MOZ_ASSERT(mPrincipal);
    mPrincipal->GetOrigin(origin);

    RefPtr<FetchService> fetchService = FetchService::GetInstance();
    MOZ_ASSERT(fetchService);
    if (fetchService->DoesExceedsKeepaliveResourceLimits(origin)) {
      FETCH_LOG(("FetchInstance::Fetch Keepalive request exceeds limit"));
      return FetchService::NetworkErrorResponse(NS_ERROR_DOM_ABORT_ERR, mArgs);
    }
    fetchService->IncrementKeepAliveRequestCount(origin);
  }

  // Create a FetchDriver instance
  mFetchDriver = MakeRefPtr<FetchDriver>(
      mRequest.clonePtr(),               // Fetch Request
      mPrincipal,                        // Principal
      mLoadGroup,                        // LoadGroup
      GetMainThreadSerialEventTarget(),  // MainThreadEventTarget
      mCookieJarSettings,                // CookieJarSettings
      mPerformanceStorage,               // PerformanceStorage
      // For service workers we set
      // tracking fetch to false, but for Keepalive
      // requests from main thread this needs to be
      // changed. See Bug 1892406
      false  // IsTrackingFetch
  );

  if (mArgsType == FetchArgsType::WorkerFetch) {
    auto& args = mArgs.as<WorkerFetchArgs>();
    mFetchDriver->SetWorkerScript(args.mWorkerScript);
    MOZ_ASSERT(args.mClientInfo.isSome());
    mFetchDriver->SetClientInfo(args.mClientInfo.ref());
    mFetchDriver->SetController(args.mController);
    if (args.mCSPEventListener) {
      mFetchDriver->SetCSPEventListener(args.mCSPEventListener);
    }
    mFetchDriver->SetAssociatedBrowsingContextID(
        args.mAssociatedBrowsingContextID);
    mFetchDriver->SetIsThirdPartyContext(Some(args.mIsThirdPartyContext));
    mFetchDriver->SetIsOn3PCBExceptionList(args.mIsOn3PCBExceptionList);
  }

  if (mArgsType == FetchArgsType::MainThreadFetch) {
    auto& args = mArgs.as<MainThreadFetchArgs>();
    mFetchDriver->SetIsThirdPartyContext(Some(args.mIsThirdPartyContext));
  }

  mFetchDriver->EnableNetworkInterceptControl();
  mPromises = MakeRefPtr<FetchServicePromises>();

  // Call FetchDriver::Fetch to start fetching.
  // Pass AbortSignalImpl as nullptr since we no need support AbortSignalImpl
  // with FetchService. AbortSignalImpl related information should be passed
  // through PFetch or InterceptedHttpChannel, then call
  // FetchService::CancelFetch() to abort the running fetch.
  rv = mFetchDriver->Fetch(nullptr, this);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FETCH_LOG(
        ("FetchInstance::Fetch FetchDriver::Fetch failed(0x%X)", (uint32_t)rv));
    return FetchService::NetworkErrorResponse(rv, mArgs);
  }

  return mPromises;
}

bool FetchService::FetchInstance::IsLocalHostFetch() const {
  if (!mPrincipal) {
    return false;
  }
  bool res;
  nsresult rv = mPrincipal->GetIsLoopbackHost(&res);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }
  return res;
}

void FetchService::FetchInstance::Cancel(bool aForceAbort) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  FETCH_LOG(("FetchInstance::Cancel() [%p]"this));

  // If mFetchDriver is not null here, FetchInstance::Fetch() has already
  // started, let mFetchDriver::RunAbortAlgorithm() to call
  // FetchInstance::OnResponseEnd() to resolve the pending promises.
  // Otherwise, resolving the pending promises here.
  if (mFetchDriver) {
    // if keepalive is active and it is NOT user initiated Abort, then
    // do not cancel the request.
    if (mRequest->GetKeepalive() && !aForceAbort) {
      FETCH_LOG(("Cleaning up the worker for keepalive[%p]"this));

      MOZ_ASSERT(mArgs.is<WorkerFetchArgs>());
      if (mArgs.is<WorkerFetchArgs>()) {
        // delete the actors for cleanup for worker keep-alive requests.
        // Non-worker keepalive requests need actors to be active until request
        // completion, because we update request quota per load-group in
        // FetchChild::ActorDestroy.
        MOZ_ASSERT((mArgs.as<WorkerFetchArgs>().mFetchParentPromise));
        if (mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Exists()) {
          FETCH_LOG(
              ("FetchInstance::Cancel() [%p] mResponseEndPromiseHolder exists",
               this));

          mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Disconnect();

          // the parent promise resolution leads to deleting of actors
          // mActorDying prevents further access to FetchParent
          mActorDying = true;
          mArgs.as<WorkerFetchArgs>().mFetchParentPromise->Resolve(true,
                                                                   __func__);
        }
      }
      return;
    }
    mFetchDriver->RunAbortAlgorithm();
    return;
  }

  MOZ_ASSERT(mPromises);

  mPromises->ResolveResponseAvailablePromise(
      InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);

  mPromises->ResolveResponseTimingPromise(ResponseTiming(), __func__);

  mPromises->ResolveResponseEndPromise(
      ResponseEndArgs(FetchDriverObserver::eAborted), __func__);
}

void FetchService::FetchInstance::OnResponseEnd(
    FetchDriverObserver::EndReason aReason,
    JS::Handle<JS::Value> aReasonDetails) {
  FETCH_LOG(("FetchInstance::OnResponseEnd [%p] %s"this,
             aReason == eAborted ? "eAborted" : "eNetworking"));

  if (mRequest->GetKeepalive()) {
    nsAutoCString origin;
    MOZ_ASSERT(mPrincipal);
    mPrincipal->GetOrigin(origin);
    RefPtr<FetchService> fetchService = FetchService::GetInstance();
    fetchService->DecrementKeepAliveRequestCount(origin);
  }

  MOZ_ASSERT(mRequest);
  if (mArgsType != FetchArgsType::NavigationPreload) {
    FlushConsoleReport();
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        __func__,
        [endArgs = ResponseEndArgs(aReason), actorID = GetActorID()]() {
          FETCH_LOG(("FetchInstance::OnResponseEnd, Runnable"));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnResponseEnd(std::move(endArgs));
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  }

  MOZ_ASSERT(mPromises);

  if (mArgs.is<WorkerFetchArgs>() &&
      mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Exists()) {
    mArgs.as<WorkerFetchArgs>().mResponseEndPromiseHolder.Complete();
  }

  if (aReason == eAborted) {
    // If ResponseAvailablePromise has not resolved yet, resolved with
    // NS_ERROR_DOM_ABORT_ERR response. If the promise is already resolved,
    // this will have no effect.
    mPromises->ResolveResponseAvailablePromise(
        InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);

    // If ResponseTimingPromise has not resolved yet, resolved with empty
    // ResponseTiming. If the promise is already resolved, this has no effect.
    mPromises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
    // Resolve the ResponseEndPromise
    mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason), __func__);
    return;
  }

  MOZ_ASSERT(mPromises->IsResponseAvailablePromiseResolved() &&
             mPromises->IsResponseTimingPromiseResolved());

  // Resolve the ResponseEndPromise
  mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason), __func__);

  // Remove the FetchInstance from FetchInstanceTable
  RefPtr<FetchService> fetchService = FetchService::GetInstance();
  MOZ_ASSERT(fetchService);
  auto entry = fetchService->mFetchInstanceTable.Lookup(mPromises);
  if (entry) {
    entry.Remove();
    FETCH_LOG(
        ("FetchInstance::OnResponseEnd entry of responsePromise[%p] is "
         "removed",
         mPromises.get()));
  }
}

void FetchService::FetchInstance::OnResponseAvailableInternal(
    SafeRefPtr<InternalResponse> aResponse) {
  FETCH_LOG(("FetchInstance::OnResponseAvailableInternal [%p]"this));
  mResponse = std::move(aResponse);

  nsCOMPtr<nsIInputStream> body;
  mResponse->GetUnfilteredBody(getter_AddRefs(body));
  FETCH_LOG(
      ("FetchInstance::OnResponseAvailableInternal [%p] response body: %p",
       this, body.get()));
  MOZ_ASSERT(mRequest);

  if (mArgsType != FetchArgsType::NavigationPreload && !mActorDying) {
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        __func__,
        [response = mResponse.clonePtr(), actorID = GetActorID()]() mutable {
          FETCH_LOG(("FetchInstance::OnResponseAvailableInternal Runnable"));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnResponseAvailableInternal(std::move(response));
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  }

  MOZ_ASSERT(mPromises);

  // Resolve the ResponseAvailablePromise
  mPromises->ResolveResponseAvailablePromise(mResponse.clonePtr(), __func__);
}

bool FetchService::FetchInstance::NeedOnDataAvailable() {
  if (mArgs.is<WorkerFetchArgs>()) {
    return mArgs.as<WorkerFetchArgs>().mNeedOnDataAvailable;
  }

  if (mArgs.is<MainThreadFetchArgs>()) {
    return mArgs.as<MainThreadFetchArgs>().mNeedOnDataAvailable;
  }

  return false;
}

void FetchService::FetchInstance::OnDataAvailable() {
  FETCH_LOG(("FetchInstance::OnDataAvailable [%p]"this));

  if (!NeedOnDataAvailable()) {
    return;
  }

  MOZ_ASSERT(mRequest);

  if (mArgsType != FetchArgsType::NavigationPreload && !mActorDying) {
    nsCOMPtr<nsIRunnable> r =
        NS_NewRunnableFunction(__func__, [actorID = GetActorID()]() {
          FETCH_LOG(("FetchInstance::OnDataAvailable, Runnable"));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnDataAvailable();
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  }
}

void FetchService::FetchInstance::FlushConsoleReport() {
  FETCH_LOG(("FetchInstance::FlushConsoleReport [%p]"this));

  if (mArgsType != FetchArgsType::NavigationPreload && !mActorDying) {
    if (!mReporter) {
      return;
    }
    nsTArray<net::ConsoleReportCollected> reports;
    mReporter->StealConsoleReports(reports);
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        __func__,
        [actorID = GetActorID(), consoleReports = std::move(reports)]() {
          FETCH_LOG(("FetchInstance::FlushConsolReport, Runnable"));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnFlushConsoleReport(std::move(consoleReports));
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  }
}

void FetchService::FetchInstance::OnReportPerformanceTiming() {
  FETCH_LOG(("FetchInstance::OnReportPerformanceTiming [%p]"this));
  MOZ_ASSERT(mFetchDriver);
  MOZ_ASSERT(mPromises);

  if (mPromises->IsResponseTimingPromiseResolved()) {
    return;
  }

  ResponseTiming timing;
  UniquePtr<PerformanceTimingData> performanceTiming(
      mFetchDriver->GetPerformanceTimingData(timing.initiatorType(),
                                             timing.entryName()));
  // FetchDriver has no corresponding performance timing when fetch() failed.
  // Resolve the ResponseTimingPromise with empty timing.
  if (!performanceTiming) {
    mPromises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
    return;
  }
  timing.timingData() = performanceTiming->ToIPC();
  // Force replace initiatorType for ServiceWorkerNavgationPreload.
  if (mArgsType == FetchArgsType::NavigationPreload) {
    timing.initiatorType() = u"navigation"_ns;
  } else if (!mActorDying) {
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        __func__, [actorID = GetActorID(), timing = timing]() {
          FETCH_LOG(("FetchInstance::OnReportPerformanceTiming, Runnable"));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnReportPerformanceTiming(std::move(timing));
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        GetBackgroundEventTarget()->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  }

  mPromises->ResolveResponseTimingPromise(std::move(timing), __func__);
}

void FetchService::FetchInstance::OnNotifyNetworkMonitorAlternateStack(
    uint64_t aChannelID) {
  FETCH_LOG(("FetchInstance::OnNotifyNetworkMonitorAlternateStack [%p]"this));
  MOZ_ASSERT(mFetchDriver);
  MOZ_ASSERT(mPromises);
  if (mArgsType != FetchArgsType::WorkerFetch) {
    // We need to support this for Main thread fetch requests as well
    // See Bug 1897129
    return;
  }

  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
      __func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID,
                 channelID = aChannelID]() {
        FETCH_LOG(
            ("FetchInstance::NotifyNetworkMonitorAlternateStack, Runnable"));
        RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
        if (actor) {
          actor->OnNotifyNetworkMonitorAlternateStack(channelID);
        }
      });

  MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
      r, nsIThread::DISPATCH_NORMAL));
}

nsID FetchService::FetchInstance::GetActorID() {
  if (mArgsType == FetchArgsType::WorkerFetch) {
    return mArgs.as<WorkerFetchArgs>().mActorID;
  }

  if (mArgsType == FetchArgsType::MainThreadFetch) {
    return mArgs.as<MainThreadFetchArgs>().mActorID;
  }

  MOZ_ASSERT_UNREACHABLE("GetActorID called for unexpected mArgsType");

  return {};
}

nsCOMPtr<nsISerialEventTarget>
FetchService::FetchInstance::GetBackgroundEventTarget() {
  if (mArgsType == FetchArgsType::WorkerFetch) {
    return mArgs.as<WorkerFetchArgs>().mEventTarget;
  }

  if (mArgsType == FetchArgsType::MainThreadFetch) {
    return mArgs.as<MainThreadFetchArgs>().mEventTarget;
  }

  MOZ_ASSERT_UNREACHABLE(
      "GetBackgroundEventTarget called for unexpected mArgsType");

  return {};
}

// FetchService

NS_IMPL_ISUPPORTS(FetchService, nsIObserver)

StaticRefPtr<FetchService> gInstance;

/*static*/
already_AddRefed<FetchService> FetchService::GetInstance() {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  if (!gInstance) {
    gInstance = MakeRefPtr<FetchService>();
    nsresult rv = gInstance->RegisterNetworkObserver();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      gInstance = nullptr;
      return nullptr;
    }
    ClearOnShutdown(&gInstance);
  }
  RefPtr<FetchService> service = gInstance;
  return service.forget();
}

/*static*/
RefPtr<FetchServicePromises> FetchService::NetworkErrorResponse(
    nsresult aRv, const FetchArgs& aArgs) {
  if (aArgs.is<WorkerFetchArgs>()) {
    const WorkerFetchArgs& args = aArgs.as<WorkerFetchArgs>();
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        __func__, [aRv, actorID = args.mActorID]() mutable {
          FETCH_LOG(
              ("FetchService::PropagateErrorResponse runnable aError: 0x%X",
               (uint32_t)aRv));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnResponseAvailableInternal(
                InternalResponse::NetworkError(aRv));
            actor->OnResponseEnd(
                ResponseEndArgs(FetchDriverObserver::eAborted));
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        args.mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  } else if (aArgs.is<MainThreadFetchArgs>()) {
    const MainThreadFetchArgs& args = aArgs.as<MainThreadFetchArgs>();
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        __func__, [aRv, actorID = args.mActorID]() mutable {
          FETCH_LOG(
              ("FetchService::PropagateErrorResponse runnable aError: 0x%X",
               (uint32_t)aRv));
          RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
          if (actor) {
            actor->OnResponseAvailableInternal(
                InternalResponse::NetworkError(aRv));
            actor->OnResponseEnd(
                ResponseEndArgs(FetchDriverObserver::eAborted));
          }
        });
    MOZ_ALWAYS_SUCCEEDS(
        args.mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL));
  }

  RefPtr<FetchServicePromises> promises = MakeRefPtr<FetchServicePromises>();
  promises->ResolveResponseAvailablePromise(InternalResponse::NetworkError(aRv),
                                            __func__);
  promises->ResolveResponseTimingPromise(ResponseTiming(), __func__);
  promises->ResolveResponseEndPromise(
      ResponseEndArgs(FetchDriverObserver::eAborted), __func__);
  return promises;
}

FetchService::FetchService() {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
}

FetchService::~FetchService() {
  MOZ_ALWAYS_SUCCEEDS(UnregisterNetworkObserver());
}

nsresult FetchService::RegisterNetworkObserver() {
  AssertIsOnMainThread();
  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  if (!observerService) {
    return NS_ERROR_UNEXPECTED;
  }

  nsCOMPtr<nsIIOService> ioService = services::GetIOService();
  if (!ioService) {
    return NS_ERROR_UNEXPECTED;
  }

  nsresult rv = observerService->AddObserver(
      this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = observerService->AddObserver(this"xpcom-shutdown"false);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = ioService->GetOffline(&mOffline);
  NS_ENSURE_SUCCESS(rv, rv);
  mObservingNetwork = true;

  return NS_OK;
}

nsresult FetchService::UnregisterNetworkObserver() {
  AssertIsOnMainThread();
  nsresult rv;
  if (mObservingNetwork) {
    nsCOMPtr<nsIObserverService> observerService =
        mozilla::services::GetObserverService();
    if (observerService) {
      rv = observerService->RemoveObserver(this,
                                           NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
      NS_ENSURE_SUCCESS(rv, rv);
      rv = observerService->RemoveObserver(this"xpcom-shutdown");
      NS_ENSURE_SUCCESS(rv, rv);
    }
    mObservingNetwork = false;
  }
  return NS_OK;
}

void FetchService::IncrementKeepAliveRequestCount(const nsACString& aOrigin) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  FETCH_LOG(("FetchService::IncrementKeepAliveRequestCount [origin=%s]\n",
             PromiseFlatCString(aOrigin).get()));
  ++mTotalKeepAliveRequests;
  uint32_t count = mPendingKeepAliveRequestsPerOrigin.Get(aOrigin) + 1;
  mPendingKeepAliveRequestsPerOrigin.InsertOrUpdate(aOrigin, count);
}

void FetchService::DecrementKeepAliveRequestCount(const nsACString& aOrigin) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  FETCH_LOG(("FetchService::DecrementKeepAliveRequestCount [origin=%s]\n",
             PromiseFlatCString(aOrigin).get()));
  MOZ_ASSERT(mTotalKeepAliveRequests > 0);
  if (mTotalKeepAliveRequests) {
    --mTotalKeepAliveRequests;
  }

  uint32_t count = mPendingKeepAliveRequestsPerOrigin.Get(aOrigin);
  MOZ_ASSERT(count > 0);
  if (count) {
    --count;
    if (count == 0) {
      mPendingKeepAliveRequestsPerOrigin.Remove(aOrigin);
    } else {
      mPendingKeepAliveRequestsPerOrigin.InsertOrUpdate(aOrigin, count);
    }
  }
}

bool FetchService::DoesExceedsKeepaliveResourceLimits(
    const nsACString& origin) {
  if (mTotalKeepAliveRequests >=
      StaticPrefs::dom_fetchKeepalive_total_request_limit()) {
    // Count keep-alive request discards due to
    // exceeding the total keep-alive request limit.
    mozilla::glean::networking::fetch_keepalive_discard_count
        .Get("total_keepalive_limit"_ns)
        .Add(1);
    return true;
  }

  if (mPendingKeepAliveRequestsPerOrigin.Get(origin) >=
      StaticPrefs::dom_fetchKeepalive_request_limit_per_origin()) {
    // Count keep-alive request discards due to
    // exceeding the per-origin request limit.
    mozilla::glean::networking::fetch_keepalive_discard_count
        .Get("per_origin_limit"_ns)
        .Add(1);
    return true;
  }

  return false;
}

NS_IMETHODIMP FetchService::Observe(nsISupports* aSubject, const char* aTopic,
                                    const char16_t* aData) {
  FETCH_LOG(("FetchService::Observe topic: %s", aTopic));
  AssertIsOnMainThread();
  MOZ_ASSERT(!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) ||
             !strcmp(aTopic, "xpcom-shutdown"));

  if (!strcmp(aTopic, "xpcom-shutdown")) {
    // Going to shutdown, unregister the network status observer to avoid
    // receiving
    nsresult rv = UnregisterNetworkObserver();
    NS_ENSURE_SUCCESS(rv, rv);
    return NS_OK;
  }

  if (nsDependentString(aData).EqualsLiteral(NS_IOSERVICE_ONLINE)) {
    mOffline = false;
  } else {
    mOffline = true;
    // Network is offline, cancel the running fetch that is not to local server.
    mFetchInstanceTable.RemoveIf([](auto& entry) {
      bool res = entry.Data()->IsLocalHostFetch();
      if (res) {
        return false;
      }
      entry.Data()->Cancel(true);
      return true;
    });
  }
  return NS_OK;
}

RefPtr<FetchServicePromises> FetchService::Fetch(FetchArgs&& aArgs) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  FETCH_LOG(("FetchService::Fetch (%s)", aArgs.is<NavigationPreloadArgs>()
                                             ? "NavigationPreload"
                                             : "WorkerFetch"));
  // Create FetchInstance
  RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>();

  // Call FetchInstance::Initialize() to get needed information for
  // FetchDriver
  nsresult rv = fetch->Initialize(std::move(aArgs));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return NetworkErrorResponse(rv, fetch->Args());
  }

  if (mOffline && !fetch->IsLocalHostFetch()) {
    FETCH_LOG(("FetchService::Fetch network offline"));
    return NetworkErrorResponse(NS_ERROR_OFFLINE, fetch->Args());
  }

  // Call FetchInstance::Fetch() to start an asynchronous fetching.
  RefPtr<FetchServicePromises> promises = fetch->Fetch();
  MOZ_ASSERT(promises);

  if (!promises->IsResponseAvailablePromiseResolved()) {
    // Insert the created FetchInstance into FetchInstanceTable.
    if (!mFetchInstanceTable.WithEntryHandle(promises, [&](auto&& entry) {
          if (entry.HasEntry()) {
            return false;
          }
          entry.Insert(fetch);
          return true;
        })) {
      FETCH_LOG(
          ("FetchService::Fetch entry[%p] already exists", promises.get()));
      return NetworkErrorResponse(NS_ERROR_UNEXPECTED, fetch->Args());
    }
    FETCH_LOG(("FetchService::Fetch entry[%p] of FetchInstance[%p] added",
               promises.get(), fetch.get()));
  }
  return promises;
}

void FetchService::CancelFetch(const RefPtr<FetchServicePromises>&& aPromises,
                               bool aForceAbort) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPromises);
  FETCH_LOG(("FetchService::CancelFetch aPromises[%p]", aPromises.get()));

  auto entry = mFetchInstanceTable.Lookup(aPromises);
  if (entry) {
    // Notice any modifications here before entry.Remove() probably should be
    // reflected to Observe() offline case.
    entry.Data()->Cancel(aForceAbort);
    entry.Remove();
    FETCH_LOG(
        ("FetchService::CancelFetch entry [%p] removed", aPromises.get()));
  }
}

MozPromiseRequestHolder<FetchServiceResponseEndPromise>&
FetchService::GetResponseEndPromiseHolder(
    const RefPtr<FetchServicePromises>& aPromises) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPromises);
  auto entry = mFetchInstanceTable.Lookup(aPromises);
  MOZ_ASSERT(entry);
  return entry.Data()->GetResponseEndPromiseHolder();
}

}  // namespace mozilla::dom

100%


¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.