Bug 1725567 - P2 FetchService interface declaration. r=dom-worker-reviewers,jesup

This patch defines the interface of FetchService for its consumers.

RefPtr<FetchServiceResponsePromise>&& FetchService::Fetch(SafeRefPtr<InternalRequest> aRequest);

FetchService is a singleton object in the parent process main thread, which is used for managing all the requested fetching created by FetchService.
It would also support more control capability, such as abort or cancel, and also observers for shutdown or other cases. These would be implemented in the following patches.

FetchInstance is an internal representation for each fetching.

Depends on D128465

Differential Revision: https://phabricator.services.mozilla.com/D128223
This commit is contained in:
Eden Chuang 2022-01-04 14:42:34 +00:00
parent d96d9736dd
commit 7245366e6f
4 changed files with 222 additions and 21 deletions

126
dom/fetch/FetchService.cpp Normal file
View File

@ -0,0 +1,126 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/FetchService.h"
#include "mozilla/dom/InternalRequest.h"
#include "mozilla/dom/InternalResponse.h"
#include "nsXULAppAPI.h"
namespace mozilla::dom {
/**
* FetchInstance is an internal representation for each Fetch created by
* FetchService.
*/
FetchService::FetchInstance::FetchInstance(SafeRefPtr<InternalRequest> aRequest)
: mRequest(std::move(aRequest)) {}
nsresult FetchService::FetchInstance::Initialize(nsIChannel* aChannel) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// TODO:
// In this method, the needed information for instancing a FetchDriver will
// be created and saved into member variables, which means principal
// LoadGroup, cookieJarSettings, performanceStorage.
// This will be implemented in the next patch.
return NS_ERROR_NOT_IMPLEMENTED;
}
FetchService::FetchInstance::~FetchInstance() = default;
RefPtr<FetchServiceResponsePromise> FetchService::FetchInstance::Fetch() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// TODO:
// After FetchInstance is initialized in proper, this method will instances a
// FetchDriver and call FetchDriver::Fetch() to start an asynchronous
// fetching. Once the fetching starts, this method returns a
// FetchServiceResponsePromise to its caller. This would be implemented in
// following patches.
return mResponsePromiseHolder.Ensure(__func__);
}
StaticRefPtr<FetchService> gInstance;
/*static*/
already_AddRefed<FetchService> FetchService::GetInstance() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
if (!gInstance) {
gInstance = MakeRefPtr<FetchService>();
ClearOnShutdown(&gInstance);
}
RefPtr<FetchService> service = gInstance;
return service.forget();
}
/*static*/
RefPtr<FetchServiceResponsePromise> FetchService::NetworkErrorResponse(
nsresult aRv) {
return FetchServiceResponsePromise::CreateAndResolve(
InternalResponse::NetworkError(aRv), __func__);
}
FetchService::FetchService() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
FetchService::~FetchService() = default;
RefPtr<FetchServiceResponsePromise> FetchService::Fetch(
SafeRefPtr<InternalRequest> aRequest, nsIChannel* aChannel) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// Create FetchInstance
RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>(aRequest.clonePtr());
// Call FetchInstance::Initialize() to get needed information for FetchDriver,
nsresult rv = fetch->Initialize(aChannel);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NetworkErrorResponse(rv);
}
// Call FetchInstance::Fetch() to start an asynchronous fetching.
RefPtr<FetchServiceResponsePromise> responsePromise = fetch->Fetch();
// Insert the created FetchInstance into FetchInstanceTable.
// TODO: the FetchInstance should be removed from FetchInstanceTable, once the
// fetching finishes or be aborted. This should be implemented in
// following patches when implementing FetchDriverObserver on
// FetchInstance
if (!mFetchInstanceTable.WithEntryHandle(responsePromise, [&](auto&& entry) {
if (entry.HasEntry()) {
return false;
}
entry.Insert(fetch);
return true;
})) {
return NetworkErrorResponse(NS_ERROR_UNEXPECTED);
}
return responsePromise;
}
void FetchService::CancelFetch(
RefPtr<FetchServiceResponsePromise>&& aResponsePromise) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aResponsePromise);
auto entry = mFetchInstanceTable.Lookup(aResponsePromise);
if (entry) {
// TODO: Need to call FetchDriver::RunAbortAlgorithm();
entry.Remove();
}
}
} // namespace mozilla::dom

View File

@ -6,25 +6,97 @@
#ifndef _mozilla_dom_FetchService_h
#define _mozilla_dom_FetchService_h
#include "nsIChannel.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "nsTHashMap.h"
/**
* TODO: This file will be moved to dom/fetch/ after FetchService implementation
*/
namespace mozilla {
class CopyableErrorResult;
namespace dom {
namespace mozilla::dom {
class InternalRequest;
class InternalResponse;
using FetchServiceResponsePromise =
MozPromise<SafeRefPtr<InternalResponse>, CopyableErrorResult, true>;
} // namespace dom
} // namespace mozilla
/**
* FetchService is a singleton object which designed to be used in parent
* process main thread only. It is used to handle the special fetch requests
* from ServiceWorkers(by Navigation Preload) and PFetch.
*
* FetchService creates FetchInstance internally to represent each Fetch
* request. It supports an asynchronous fetching, FetchServiceResponsePromise is
* created when a Fetch starts, once the response is ready or any error happens,
* the FetchServiceResponsePromise would be resolved or rejected. The promise
* consumers can set callbacks to handle the Fetch result.
*/
class FetchService final {
public:
NS_INLINE_DECL_REFCOUNTING(FetchService)
static already_AddRefed<FetchService> GetInstance();
static RefPtr<FetchServiceResponsePromise> NetworkErrorResponse(nsresult aRv);
FetchService();
// This method creates a FetchInstance to trigger fetch.
// The created FetchInstance is saved in mFetchInstanceTable
RefPtr<FetchServiceResponsePromise> Fetch(
SafeRefPtr<InternalRequest> aRequest, nsIChannel* aChannel = nullptr);
void CancelFetch(RefPtr<FetchServiceResponsePromise>&& aResponsePromise);
private:
/**
* FetchInstance is an internal representation for each Fetch created by
* FetchService.
* FetchInstance is also a FetchDriverObserver which has responsibility to
* resolve/reject the FetchServiceResponsePromise.
* FetchInstance triggers fetch by instancing a FetchDriver with proper
* initialization. The general usage flow of FetchInstance is as follows
*
* RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>(aResquest);
* fetch->Initialize();
* RefPtr<FetchServiceResponsePromise> fetch->Fetch();
*/
// TODO:
// FetchInstance should inherit FetchDriverObserver.
// Will be implemented in following patches.
class FetchInstance final {
NS_INLINE_DECL_REFCOUNTING(FetchInstance);
public:
explicit FetchInstance(SafeRefPtr<InternalRequest> aRequest);
// This method is used for initialize the fetch.
// It accepts a nsIChannel for initialization, this is for the navigation
// preload case since there has already been an intercepted channel for
// dispatching fetch event, and needed information can be gotten from the
// intercepted channel.
// For other case, the fetch needed information be created according to the
// mRequest
nsresult Initialize(nsIChannel* aChannel = nullptr);
RefPtr<FetchServiceResponsePromise> Fetch();
private:
~FetchInstance();
SafeRefPtr<InternalRequest> mRequest;
MozPromiseHolder<FetchServiceResponsePromise> mResponsePromiseHolder;
};
~FetchService();
// This is a container to manage the generated fetches.
nsTHashMap<nsRefPtrHashKey<FetchServiceResponsePromise>,
RefPtr<FetchInstance> >
mFetchInstanceTable;
};
} // namespace mozilla::dom
#endif // _mozilla_dom_FetchService_h

View File

@ -32,6 +32,7 @@ UNIFIED_SOURCES += [
"Fetch.cpp",
"FetchDriver.cpp",
"FetchObserver.cpp",
"FetchService.cpp",
"FetchStreamReader.cpp",
"FetchStreamUtils.cpp",
"FetchUtil.cpp",

View File

@ -925,7 +925,7 @@ ServiceWorkerPrivateImpl::SetupNavigationPreload(
// Fail to get the request's body, stop navigation preload by returning
// nullptr.
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
return FetchService::NetworkErrorResponse(rv);
}
preloadRequest->SetBody(uploadStream, ipcRequest.bodySize());
}
@ -944,11 +944,15 @@ ServiceWorkerPrivateImpl::SetupNavigationPreload(
aRegistration->GetNavigationPreloadState().headerValue(), err);
preloadRequest->Headers()->SetGuard(headersGuard, err);
// TODO:
// Step 3. Perform fetch through FetchService with the cloned request
if (!err.Failed()) {
nsCOMPtr<nsIChannel> underlyingChannel;
MOZ_ALWAYS_SUCCEEDS(
aChannel->GetChannel(getter_AddRefs(underlyingChannel)));
RefPtr<FetchService> fetchService = FetchService::GetInstance();
return fetchService->Fetch(std::move(preloadRequest), underlyingChannel);
}
return nullptr;
return FetchService::NetworkErrorResponse(NS_ERROR_UNEXPECTED);
}
nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
@ -979,8 +983,10 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
bool preloadNavigation = isNonSubresourceRequest &&
request.method().LowerCaseEqualsASCII("get") &&
aRegistration->GetNavigationPreloadState().enabled();
RefPtr<FetchServiceResponsePromise> preloadResponsePromise;
if (preloadNavigation) {
SetupNavigationPreload(aChannel, aRegistration);
preloadResponsePromise = SetupNavigationPreload(aChannel, aRegistration);
}
ParentToParentServiceWorkerFetchEventOpArgs args(
@ -994,9 +1000,7 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
UniquePtr<PendingFunctionalEvent> pendingEvent =
MakeUnique<PendingFetchEvent>(this, std::move(aRegistration),
std::move(args), std::move(aChannel),
// The FetchServiceResponsePromise from
// FetchService
nullptr);
std::move(preloadResponsePromise));
mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
@ -1007,9 +1011,7 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
return SendFetchEventInternal(std::move(aRegistration), std::move(args),
std::move(aChannel),
// The FetchServiceResponsePromise from
// FetchService
nullptr);
std::move(preloadResponsePromise));
}
nsresult ServiceWorkerPrivateImpl::SendFetchEventInternal(