Bug 1351231 - FetchService integration for PFetch. r=dom-worker-reviewers,jesup

Depends on D142436

Differential Revision: https://phabricator.services.mozilla.com/D142437
This commit is contained in:
Eden Chuang 2023-01-20 09:09:17 +00:00
parent 22c302924c
commit 2ca58b8fde
14 changed files with 481 additions and 129 deletions

View File

@ -39,6 +39,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/dom/ServiceWorkerInterceptController.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/PreloaderBase.h"
@ -319,7 +320,8 @@ AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; }
//-----------------------------------------------------------------------------
NS_IMPL_ISUPPORTS(FetchDriver, nsIStreamListener, nsIChannelEventSink,
nsIInterfaceRequestor, nsIThreadRetargetableStreamListener)
nsIInterfaceRequestor, nsIThreadRetargetableStreamListener,
nsINetworkInterceptController)
FetchDriver::FetchDriver(SafeRefPtr<InternalRequest> aRequest,
nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup,
@ -1464,6 +1466,55 @@ void FetchDriver::FinishOnStopRequest(
Unfollow();
}
NS_IMETHODIMP
FetchDriver::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
bool* aShouldIntercept) {
MOZ_ASSERT(aChannel);
if (mInterceptController) {
MOZ_ASSERT(XRE_IsParentProcess());
return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
aShouldIntercept);
}
nsCOMPtr<nsINetworkInterceptController> controller;
NS_QueryNotificationCallbacks(nullptr, mLoadGroup,
NS_GET_IID(nsINetworkInterceptController),
getter_AddRefs(controller));
if (controller) {
return controller->ShouldPrepareForIntercept(aURI, aChannel,
aShouldIntercept);
}
*aShouldIntercept = false;
return NS_OK;
}
NS_IMETHODIMP
FetchDriver::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
if (mInterceptController) {
MOZ_ASSERT(XRE_IsParentProcess());
return mInterceptController->ChannelIntercepted(aChannel);
}
nsCOMPtr<nsINetworkInterceptController> controller;
NS_QueryNotificationCallbacks(nullptr, mLoadGroup,
NS_GET_IID(nsINetworkInterceptController),
getter_AddRefs(controller));
if (controller) {
return controller->ChannelIntercepted(aChannel);
}
return NS_OK;
}
void FetchDriver::EnableNetworkInterceptControl() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mInterceptController);
mInterceptController = new ServiceWorkerInterceptController();
}
NS_IMETHODIMP
FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
nsIChannel* aNewChannel, uint32_t aFlags,
@ -1577,7 +1628,9 @@ void FetchDriver::SetController(
PerformanceTimingData* FetchDriver::GetPerformanceTimingData(
nsAString& aInitiatorType, nsAString& aEntryName) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(mChannel);
if (!mChannel) {
return nullptr;
}
nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
if (!timedChannel) {

View File

@ -9,6 +9,7 @@
#include "nsIChannelEventSink.h"
#include "nsIInterfaceRequestor.h"
#include "nsINetworkInterceptController.h"
#include "nsIStreamListener.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "mozilla/ConsoleReportCollector.h"
@ -92,6 +93,7 @@ class AlternativeDataStreamListener;
class FetchDriver final : public nsIStreamListener,
public nsIChannelEventSink,
public nsIInterfaceRequestor,
public nsINetworkInterceptController,
public nsIThreadRetargetableStreamListener,
public AbortFollower {
public:
@ -100,6 +102,7 @@ class FetchDriver final : public nsIStreamListener,
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSINETWORKINTERCEPTCONTROLLER
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
FetchDriver(SafeRefPtr<InternalRequest> aRequest, nsIPrincipal* aPrincipal,
@ -133,6 +136,8 @@ class FetchDriver final : public nsIStreamListener,
void RunAbortAlgorithm() override;
void FetchDriverAbortActions(AbortSignalImpl* aSignalImpl);
void EnableNetworkInterceptControl();
private:
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsILoadGroup> mLoadGroup;
@ -181,6 +186,7 @@ class FetchDriver final : public nsIStreamListener,
bool mResponseAvailableCalled;
bool mFetchCalled;
#endif
nsCOMPtr<nsINetworkInterceptController> mInterceptController;
friend class AlternativeDataStreamListener;

View File

@ -4,6 +4,7 @@
#include "FetchLog.h"
#include "FetchParent.h"
#include "FetchService.h"
#include "InternalRequest.h"
#include "InternalResponse.h"
#include "mozilla/SchedulerGroup.h"
@ -79,8 +80,7 @@ FetchParent::FetchParent() : mID(nsID::GenerateUUID()) {
FetchParent::~FetchParent() {
FETCH_LOG(("FetchParent::~FetchParent [%p]", this));
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mBackgroundEventTarget);
// MOZ_ASSERT(!mBackgroundEventTarget);
MOZ_ASSERT(!mResponsePromises);
MOZ_ASSERT(mActorDestroyed && mIsDone);
}
@ -101,6 +101,7 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
if (aArgs.controller().isSome()) {
mController = Some(ServiceWorkerDescriptor(aArgs.controller().ref()));
}
mCookieJarSettings = aArgs.cookieJarSettings();
mNeedOnDataAvailable = aArgs.needOnDataAvailable();
mHasCSPEventListener = aArgs.hasCSPEventListener();
@ -119,8 +120,18 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
FETCH_LOG(
("FetchParent::RecvFetchOp [%p] Success Callback", self.get()));
AssertIsOnBackgroundThread();
self->mPromise = nullptr;
if (self->mIsDone) {
FETCH_LOG(("FetchParent::RecvFetchOp [%p] Fetch has already aborted",
self.get()));
if (!self->mActorDestroyed) {
Unused << NS_WARN_IF(
!self->Send__delete__(self, NS_ERROR_DOM_ABORT_ERR));
}
return;
}
self->mIsDone = true;
if (!self->mActorDestroyed) {
if (!self->mActorDestroyed && !self->mExtendForCSPEventListener) {
FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(NS_OK)",
self.get()));
Unused << NS_WARN_IF(!self->Send__delete__(self, NS_OK));
@ -131,6 +142,7 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
("FetchParent::RecvFetchOp [%p] Failure Callback", self.get()));
AssertIsOnBackgroundThread();
self->mIsDone = true;
self->mPromise = nullptr;
if (!self->mActorDestroyed) {
FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(aErr)",
self.get()));
@ -138,7 +150,7 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
}
});
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable {
RefPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable {
FETCH_LOG(
("FetchParent::RecvFetchOp [%p], Main Thread Runnable", self.get()));
AssertIsOnMainThread();
@ -152,8 +164,31 @@ IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) {
self->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
return;
}
// TODO: Initialize a fetch through FetchService::Fetch, and saving
// returned promises into mResponsePromises
RefPtr<FetchService> fetchService = FetchService::GetInstance();
MOZ_ASSERT(fetchService);
MOZ_ASSERT(!self->mResponsePromises);
self->mResponsePromises =
fetchService->Fetch(AsVariant(FetchService::WorkerFetchArgs(
{self->mRequest.clonePtr(), self->mPrincipalInfo,
self->mWorkerScript, self->mClientInfo, self->mController,
self->mCookieJarSettings, self->mNeedOnDataAvailable,
self->mCSPEventListener, self->mBackgroundEventTarget,
self->mID})));
self->mResponsePromises->GetResponseEndPromise()->Then(
self->mBackgroundEventTarget, __func__,
[self](ResponseEndArgs&& aArgs) mutable {
MOZ_ASSERT(self->mPromise);
self->mPromise->Resolve(true, __func__);
self->mResponsePromises = nullptr;
self->mPromise = nullptr;
},
[self](CopyableErrorResult&& aErr) mutable {
MOZ_ASSERT(self->mPromise);
self->mPromise->Reject(aErr.StealNSResult(), __func__);
self->mResponsePromises = nullptr;
self->mPromise = nullptr;
});
});
MOZ_ALWAYS_SUCCEEDS(
@ -172,29 +207,90 @@ IPCResult FetchParent::RecvAbortFetchOp() {
}
mIsDone = true;
if (mResponsePromises) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [promises = std::move(mResponsePromises)]() mutable {
FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable"));
AssertIsOnMainThread();
RefPtr<FetchParent> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [self]() mutable {
FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable"));
AssertIsOnMainThread();
if (self->mResponsePromises) {
RefPtr<FetchService> fetchService = FetchService::GetInstance();
MOZ_ASSERT(fetchService);
fetchService->CancelFetch(std::move(promises));
});
MOZ_ALWAYS_SUCCEEDS(
SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
}
fetchService->CancelFetch(std::move(self->mResponsePromises));
}
});
MOZ_ALWAYS_SUCCEEDS(
SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
return IPC_OK();
}
void FetchParent::OnResponseAvailableInternal(
SafeRefPtr<InternalResponse>&& aResponse) {
FETCH_LOG(("FetchParent::OnResponseAvailableInternal [%p]", this));
AssertIsOnBackgroundThread();
MOZ_ASSERT(aResponse);
MOZ_ASSERT(!mActorDestroyed);
if (mIsDone && aResponse->Type() != ResponseType::Error) {
FETCH_LOG(
("FetchParent::OnResponseAvailableInternal [%p] "
"Fetch has already aborted",
this));
return;
}
// To monitor the stream status between processes, response's body can not be
// serialized as RemoteLazyInputStream. Such that stream close can be
// propagated to FetchDriver in the parent process.
aResponse->SetSerializeAsLazy(false);
// CSP violation notification is asynchronous. Extending the FetchParent's
// life cycle for the notificaiton.
if (aResponse->Type() == ResponseType::Error &&
aResponse->GetErrorCode() == NS_ERROR_CONTENT_BLOCKED &&
mCSPEventListener) {
FETCH_LOG(
("FetchParent::OnResponseAvailableInternal [%p] "
"NS_ERROR_CONTENT_BLOCKED",
this));
mExtendForCSPEventListener = true;
}
Unused << SendOnResponseAvailableInternal(
aResponse->ToParentToChildInternalResponse(WrapNotNull(Manager())));
}
void FetchParent::OnResponseEnd(const ResponseEndArgs& aArgs) {
FETCH_LOG(("FetchParent::OnResponseEnd [%p]", this));
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mActorDestroyed);
if (mIsDone && aArgs.endReason() != FetchDriverObserver::eAborted) {
FETCH_LOG(
("FetchParent::OnResponseEnd [%p] "
"Fetch has already aborted",
this));
return;
}
Unused << SendOnResponseEnd(aArgs);
}
void FetchParent::OnDataAvailable() {
FETCH_LOG(("FetchParent::OnDataAvailable [%p]", this));
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mActorDestroyed);
Unused << SendOnDataAvailable();
}
void FetchParent::OnFlushConsoleReport(
nsTArray<net::ConsoleReportCollected>&& aReports) {
const nsTArray<net::ConsoleReportCollected>& aReports) {
FETCH_LOG(("FetchParent::OnFlushConsoleReport [%p]", this));
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mActorDestroyed);
Unused << SendOnFlushConsoleReport(std::move(aReports));
Unused << SendOnFlushConsoleReport(aReports);
}
void FetchParent::ActorDestroy(ActorDestroyReason aReason) {
@ -209,7 +305,7 @@ void FetchParent::ActorDestroy(ActorDestroyReason aReason) {
// Force to abort the existing fetch.
// Actor can be destoried by shutdown when still fetching.
RecvAbortFetchOp();
mBackgroundEventTarget = nullptr;
// mBackgroundEventTarget = nullptr;
}
nsICSPEventListener* FetchParent::GetCSPEventListener() {

View File

@ -7,19 +7,23 @@
#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/FetchService.h"
#include "mozilla/dom/PFetchParent.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsCOMPtr.h"
#include "nsIContentSecurityPolicy.h"
#include "nsID.h"
#include "nsISerialEventTarget.h"
#include "nsString.h"
#include "nsTHashMap.h"
namespace mozilla::dom {
class ClientInfo;
class FetchServicePromises;
class InternalRequest;
class InternalResponse;
class ServiceWorkerDescriptor;
@ -38,7 +42,14 @@ class FetchParent final : public PFetchParent {
static RefPtr<FetchParent> GetActorByID(const nsID& aID);
void OnFlushConsoleReport(nsTArray<net::ConsoleReportCollected>&& aReports);
void OnResponseAvailableInternal(SafeRefPtr<InternalResponse>&& aResponse);
void OnResponseEnd(const ResponseEndArgs& aArgs);
void OnDataAvailable();
void OnFlushConsoleReport(
const nsTArray<net::ConsoleReportCollected>& aReports);
class FetchParentCSPEventListener final : public nsICSPEventListener {
public:
@ -76,9 +87,11 @@ class FetchParent final : public PFetchParent {
nsCString mWorkerScript;
Maybe<ClientInfo> mClientInfo;
Maybe<ServiceWorkerDescriptor> mController;
RefPtr<FetchParentCSPEventListener> mCSPEventListener;
Maybe<CookieJarSettingsArgs> mCookieJarSettings;
nsCOMPtr<nsICSPEventListener> mCSPEventListener;
bool mNeedOnDataAvailable{false};
bool mHasCSPEventListener{false};
bool mExtendForCSPEventListener{false};
Atomic<bool> mIsDone{false};
Atomic<bool> mActorDestroyed{false};

View File

@ -3,7 +3,9 @@
* 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"
@ -16,14 +18,18 @@
#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/ipc/BackgroundUtils.h"
#include "mozilla/net/CookieJarSettings.h"
namespace mozilla::dom {
@ -33,8 +39,9 @@ mozilla::LazyLogModule gFetchLog("Fetch");
FetchServicePromises::FetchServicePromises()
: mAvailablePromise(
new FetchServiceResponseAvailablePromise::Private(__func__)),
mEndPromise(new FetchServiceResponseEndPromise::Private(__func__)) {
MakeRefPtr<FetchServiceResponseAvailablePromise::Private>(__func__)),
mEndPromise(
MakeRefPtr<FetchServiceResponseEndPromise::Private>(__func__)) {
mAvailablePromise->UseSynchronousTaskDispatch(__func__);
mEndPromise->UseSynchronousTaskDispatch(__func__);
}
@ -79,25 +86,26 @@ void FetchServicePromises::RejectResponseEndPromise(
// FetchInstance
FetchService::FetchInstance::FetchInstance(SafeRefPtr<InternalRequest> aRequest)
: mRequest(std::move(aRequest)) {}
FetchService::FetchInstance::~FetchInstance() = default;
nsresult FetchService::FetchInstance::Initialize(nsIChannel* aChannel) {
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 (aChannel) {
FETCH_LOG(("FetchInstance::Initialize [%p] aChannel[%p]", this, aChannel));
if (mArgs.is<NavigationPreloadArgs>()) {
mRequest = mArgs.as<NavigationPreloadArgs>().mRequest.clonePtr();
nsIChannel* channel = mArgs.as<NavigationPreloadArgs>().mChannel;
FETCH_LOG(("FetchInstance::Initialize [%p] request[%p], channel[%p]", this,
mRequest.unsafeGetRawPtr(), channel));
nsresult rv;
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
MOZ_ASSERT(loadInfo);
nsCOMPtr<nsIURI> channelURI;
rv = aChannel->GetURI(getter_AddRefs(channelURI));
rv = channel->GetURI(getter_AddRefs(channelURI));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -105,7 +113,7 @@ nsresult FetchService::FetchInstance::Initialize(nsIChannel* aChannel) {
nsIScriptSecurityManager* securityManager =
nsContentUtils::GetSecurityManager();
if (securityManager) {
securityManager->GetChannelResultPrincipal(aChannel,
securityManager->GetChannelResultPrincipal(channel,
getter_AddRefs(mPrincipal));
}
@ -114,7 +122,7 @@ nsresult FetchService::FetchInstance::Initialize(nsIChannel* aChannel) {
}
// Get loadGroup from channel
rv = aChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
rv = channel->GetLoadGroup(getter_AddRefs(mLoadGroup));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -134,10 +142,28 @@ nsresult FetchService::FetchInstance::Initialize(nsIChannel* aChannel) {
// Get PerformanceStorage from channel
mPerformanceStorage = loadInfo->GetPerformanceStorage();
} else {
// TODO:
// Get information from InternalRequest and PFetch IPC parameters.
// This will be implemented in bug 1351231.
return NS_ERROR_NOT_IMPLEMENTED;
mIsWorkerFetch = true;
mRequest = mArgs.as<WorkerFetchArgs>().mRequest.clonePtr();
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;
@ -170,6 +196,21 @@ RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
false // IsTrackingFetch
);
if (mIsWorkerFetch) {
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->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
@ -182,8 +223,6 @@ RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
return FetchService::NetworkErrorResponse(rv);
}
mPromises = MakeRefPtr<FetchServicePromises>();
return mPromises;
}
@ -197,74 +236,157 @@ void FetchService::FetchInstance::Cancel() {
mFetchDriver->RunAbortAlgorithm();
}
if (mPromises) {
mPromises->ResolveResponseAvailablePromise(
InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
MOZ_ASSERT(mPromises);
mPromises->ResolveResponseEndPromise(
ResponseEndArgs(FetchDriverObserver::eAborted, Nothing()), __func__);
mPromises = nullptr;
}
mPromises->ResolveResponseAvailablePromise(
InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
mPromises->ResolveResponseEndPromise(
ResponseEndArgs(FetchDriverObserver::eAborted, Nothing()), __func__);
}
void FetchService::FetchInstance::OnResponseEnd(
FetchDriverObserver::EndReason aReason,
JS::Handle<JS::Value> aReasonDetails) {
FETCH_LOG(("FetchInstance::OnResponseEnd [%p]", this));
if (aReason == eAborted) {
FETCH_LOG(("FetchInstance::OnResponseEnd end with eAborted"));
if (mPromises) {
mPromises->ResolveResponseEndPromise(
ResponseEndArgs(FetchDriverObserver::eAborted, Nothing()), __func__);
}
return;
}
if (mPromises) {
// Remove the FetchInstance from FetchInstanceTable
RefPtr<FetchService> fetchService = FetchService::GetInstance();
MOZ_ASSERT(fetchService);
auto entry = fetchService->mFetchInstanceTable.Lookup(mPromises);
MOZ_ASSERT(entry);
entry.Remove();
FETCH_LOG(
("FetchInstance::OnResponseEnd entry[%p] of FetchInstance[%p] is "
"removed",
mPromises.get(), this));
FETCH_LOG(("FetchInstance::OnResponseEnd [%p] %s", this,
aReason == eAborted ? "eAborted" : "eNetworking"));
// Get PerformanceTimingData from FetchDriver.
// Get response timing form FetchDriver
Maybe<ResponseTiming> responseTiming;
if (aReason != eAborted) {
ResponseTiming timing;
UniquePtr<PerformanceTimingData> performanceTiming(
mFetchDriver->GetPerformanceTimingData(timing.initiatorType(),
timing.entryName()));
if (performanceTiming != nullptr) {
timing.timingData() = performanceTiming->ToIPC();
if (!mIsWorkerFetch) {
// Force replace initiatorType for ServiceWorkerNavgationPreload.
timing.initiatorType() = u"navigation"_ns;
}
responseTiming = Some(timing);
}
}
timing.initiatorType() = u"navigation"_ns;
if (mIsWorkerFetch) {
FlushConsoleReport();
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [endArgs = ResponseEndArgs(aReason, responseTiming),
actorID = mArgs.as<WorkerFetchArgs>().mActorID]() {
FETCH_LOG(("FetchInstance::OnResponseEnd, Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnResponseEnd(std::move(endArgs));
}
});
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
// Resolve the ResponseEndPromise
mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason, Some(timing)),
__func__);
// Release promises
mPromises = nullptr;
MOZ_ASSERT(mPromises);
// Resolve the ResponseEndPromise
mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason, responseTiming),
__func__);
if (aReason == eAborted) {
return;
}
// 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));
if (mPromises) {
// Resolve the ResponseAvailablePromise
mPromises->ResolveResponseAvailablePromise(std::move(aResponse), __func__);
mResponse = std::move(aResponse);
nsCOMPtr<nsIInputStream> body;
mResponse->GetUnfilteredBody(getter_AddRefs(body));
FETCH_LOG(
("FetchInstance::OnResponseAvailableInternal [%p] response body: %p",
this, body.get()));
if (mIsWorkerFetch) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [response = mResponse.clonePtr(),
actorID = mArgs.as<WorkerFetchArgs>().mActorID]() mutable {
FETCH_LOG(("FetchInstance::OnResponseAvailableInternal Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnResponseAvailableInternal(std::move(response));
}
});
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->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;
}
return false;
}
void FetchService::FetchInstance::OnDataAvailable() {
FETCH_LOG(("FetchInstance::OnDataAvailable [%p]", this));
if (!NeedOnDataAvailable()) {
return;
}
if (mIsWorkerFetch) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID]() {
FETCH_LOG(("FetchInstance::OnDataAvailable, Runnable"));
RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID);
if (actor) {
actor->OnDataAvailable();
}
});
MOZ_ALWAYS_SUCCEEDS(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
}
// TODO:
// Following methods would not be used for navigation preload, but would be used
// with PFetch. They will be implemented in bug 1351231.
bool FetchService::FetchInstance::NeedOnDataAvailable() { return false; }
void FetchService::FetchInstance::OnDataAvailable() {}
void FetchService::FetchInstance::FlushConsoleReport() {}
void FetchService::FetchInstance::FlushConsoleReport() {
FETCH_LOG(("FetchInstance::FlushConsoleReport [%p]", this));
if (mIsWorkerFetch) {
if (!mReporter) {
return;
}
nsTArray<net::ConsoleReportCollected> reports;
mReporter->StealConsoleReports(reports);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [actorID = mArgs.as<WorkerFetchArgs>().mActorID,
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(mArgs.as<WorkerFetchArgs>().mEventTarget->Dispatch(
r, nsIThread::DISPATCH_NORMAL));
}
}
// FetchService
@ -382,31 +504,30 @@ NS_IMETHODIMP FetchService::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
RefPtr<FetchServicePromises> FetchService::Fetch(
SafeRefPtr<InternalRequest> aRequest, nsIChannel* aChannel) {
RefPtr<FetchServicePromises> FetchService::Fetch(FetchArgs&& aArgs) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
FETCH_LOG(("FetchService::Fetch aRequest[%p], aChannel[%p], mOffline: %s",
aRequest.unsafeGetRawPtr(), aChannel,
mOffline ? "true" : "false"));
FETCH_LOG(("FetchService::Fetch (%s)", aArgs.is<NavigationPreloadArgs>()
? "NavigationPreload"
: "WorkerFetch"));
if (mOffline) {
FETCH_LOG(("FetchService::Fetch network offline"));
return NetworkErrorResponse(NS_ERROR_OFFLINE);
}
// Create FetchInstance
RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>(aRequest.clonePtr());
RefPtr<FetchInstance> fetch = MakeRefPtr<FetchInstance>();
// Call FetchInstance::Initialize() to get needed information for FetchDriver,
nsresult rv = fetch->Initialize(aChannel);
nsresult rv = fetch->Initialize(std::move(aArgs));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NetworkErrorResponse(rv);
}
// Call FetchInstance::Fetch() to start an asynchronous fetching.
RefPtr<FetchServicePromises> promises = fetch->Fetch();
MOZ_ASSERT(promises);
if (!promises->GetResponseAvailablePromise()->IsResolved()) {
// Insert the created FetchInstance into FetchInstanceTable.
@ -427,7 +548,7 @@ RefPtr<FetchServicePromises> FetchService::Fetch(
return promises;
}
void FetchService::CancelFetch(RefPtr<FetchServicePromises>&& aPromises) {
void FetchService::CancelFetch(const RefPtr<FetchServicePromises>&& aPromises) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPromises);
@ -438,7 +559,6 @@ void FetchService::CancelFetch(RefPtr<FetchServicePromises>&& aPromises) {
// Notice any modifications here before entry.Remove() probably should be
// reflected to Observe() offline case.
entry.Data()->Cancel();
entry.Remove();
FETCH_LOG(
("FetchService::CancelFetch entry [%p] removed", aPromises.get()));

View File

@ -14,6 +14,8 @@
#include "mozilla/dom/FetchTypes.h"
#include "mozilla/dom/PerformanceTimingTypes.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/net/NeckoChannelParams.h"
class nsILoadGroup;
class nsIPrincipal;
@ -24,6 +26,8 @@ namespace mozilla::dom {
class InternalRequest;
class InternalResponse;
class ClientInfo;
class ServiceWorkerDescriptor;
using FetchServiceResponse = SafeRefPtr<InternalResponse>;
using FetchServiceResponseAvailablePromise =
@ -69,8 +73,31 @@ class FetchServicePromises final {
*/
class FetchService final : public nsIObserver {
public:
NS_DECL_ISUPPORTS;
NS_DECL_NSIOBSERVER;
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
struct NavigationPreloadArgs {
SafeRefPtr<InternalRequest> mRequest;
nsCOMPtr<nsIChannel> mChannel;
};
struct WorkerFetchArgs {
SafeRefPtr<InternalRequest> mRequest;
mozilla::ipc::PrincipalInfo mPrincipalInfo;
nsCString mWorkerScript;
Maybe<ClientInfo> mClientInfo;
Maybe<ServiceWorkerDescriptor> mController;
Maybe<net::CookieJarSettingsArgs> mCookieJarSettings;
bool mNeedOnDataAvailable;
nsCOMPtr<nsICSPEventListener> mCSPEventListener;
nsCOMPtr<nsISerialEventTarget> mEventTarget;
nsID mActorID;
};
struct UnknownArgs {};
using FetchArgs =
Variant<NavigationPreloadArgs, WorkerFetchArgs, UnknownArgs>;
static already_AddRefed<FetchService> GetInstance();
@ -80,10 +107,9 @@ class FetchService final : public nsIObserver {
// This method creates a FetchInstance to trigger fetch.
// The created FetchInstance is saved in mFetchInstanceTable
RefPtr<FetchServicePromises> Fetch(SafeRefPtr<InternalRequest> aRequest,
nsIChannel* aChannel = nullptr);
RefPtr<FetchServicePromises> Fetch(FetchArgs&& aArgs);
void CancelFetch(RefPtr<FetchServicePromises>&& aPromises);
void CancelFetch(const RefPtr<FetchServicePromises>&& aPromises);
private:
/**
@ -94,22 +120,15 @@ class FetchService final : public nsIObserver {
* 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<FetchInstance> fetch = MakeRefPtr<FetchInstance>();
* fetch->Initialize(FetchArgs args);
* RefPtr<FetchServicePromises> fetch->Fetch();
*/
class FetchInstance final : public FetchDriverObserver {
public:
explicit FetchInstance(SafeRefPtr<InternalRequest> aRequest);
FetchInstance() = default;
// 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);
nsresult Initialize(FetchArgs&& aArgs);
RefPtr<FetchServicePromises> Fetch();
@ -125,17 +144,18 @@ class FetchService final : public nsIObserver {
void FlushConsoleReport() override;
private:
~FetchInstance();
~FetchInstance() = default;
SafeRefPtr<InternalRequest> mRequest;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsICookieJarSettings> mCookieJarSettings;
RefPtr<PerformanceStorage> mPerformanceStorage;
FetchArgs mArgs{AsVariant(FetchService::UnknownArgs())};
RefPtr<FetchDriver> mFetchDriver;
SafeRefPtr<InternalResponse> mResponse;
RefPtr<FetchServicePromises> mPromises;
bool mIsWorkerFetch{false};
};
~FetchService();

View File

@ -9,6 +9,7 @@
#include "mozilla/RemoteLazyInputStreamStorage.h"
#include "mozilla/dom/FetchTypes.h"
#include "mozilla/dom/IPCBlob.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "nsContentUtils.h"
#include "nsXULAppAPI.h"
@ -34,7 +35,12 @@ NotNull<nsCOMPtr<nsIInputStream>> ToInputStream(
NotNull<nsCOMPtr<nsIInputStream>> ToInputStream(
const ParentToChildStream& aStream) {
MOZ_ASSERT(XRE_IsContentProcess());
nsCOMPtr<nsIInputStream> result = aStream.stream();
nsCOMPtr<nsIInputStream> result;
if (aStream.type() == ParentToChildStream::TRemoteLazyInputStream) {
result = aStream.get_RemoteLazyInputStream();
} else {
result = DeserializeIPCStream(aStream.get_IPCStream());
}
return WrapNotNull(result);
}
@ -50,11 +56,20 @@ ParentToParentStream ToParentToParentStream(
ParentToChildStream ToParentToChildStream(
const NotNull<nsCOMPtr<nsIInputStream>>& aStream, int64_t aStreamSize,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent) {
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent,
bool aSerializeAsLazy) {
MOZ_ASSERT(XRE_IsParentProcess());
ParentToChildStream result;
result.stream() = RemoteLazyInputStream::WrapStream(aStream.get());
if (aSerializeAsLazy) {
result = RemoteLazyInputStream::WrapStream(aStream.get());
} else {
nsCOMPtr<nsIInputStream> stream(aStream.get());
mozilla::ipc::IPCStream ipcStream;
Unused << NS_WARN_IF(
!mozilla::ipc::SerializeIPCStream(stream.forget(), ipcStream, false));
result = ipcStream;
}
return result;
}

View File

@ -40,7 +40,8 @@ ParentToParentStream ToParentToParentStream(
// process. Can only be called in the parent process.
ParentToChildStream ToParentToChildStream(
const NotNull<nsCOMPtr<nsIInputStream>>& aStream, int64_t aStreamSize,
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent);
NotNull<mozilla::ipc::PBackgroundParent*> aBackgroundParent,
bool aSerializeAsLazy = true);
// Convert a ParentToParentStream to a ParentToChildStream. Can only be called
// in the parent process.

View File

@ -35,8 +35,9 @@ struct ParentToParentStream {
nsID uuid;
};
struct ParentToChildStream {
RemoteLazyInputStream stream;
union ParentToChildStream {
RemoteLazyInputStream;
IPCStream;
};
struct ChildToParentStream {
@ -86,6 +87,9 @@ struct InternalResponseMetadata {
nsCString alternativeDataType;
nsITransportSecurityInfo securityInfo;
PrincipalInfo? principalInfo;
nsCString bodyBlobURISpec;
nsString bodyLocalPath;
RequestCredentials credentialsMode;
};
struct ParentToParentInternalResponse {

View File

@ -193,7 +193,7 @@ InternalRequest::InternalRequest(const IPCInternalRequest& aIPCRequest)
if (!XRE_IsParentProcess()) {
if (body) {
MOZ_ASSERT(body->type() == BodyStreamVariant::TParentToChildStream);
mBodyStream = body->get_ParentToChildStream().stream();
mBodyStream = body->get_ParentToChildStream().get_RemoteLazyInputStream();
}
} else {
if (body) {

View File

@ -90,6 +90,13 @@ template <typename T>
aIPCResponse.metadata().principalInfo().ref()));
}
nsAutoCString bodyBlobURISpec(aIPCResponse.metadata().bodyBlobURISpec());
response->SetBodyBlobURISpec(bodyBlobURISpec);
nsAutoString bodyLocalPath(aIPCResponse.metadata().bodyLocalPath());
response->SetBodyLocalPath(bodyLocalPath);
response->mCredentialsMode = aIPCResponse.metadata().credentialsMode();
switch (aIPCResponse.metadata().type()) {
case ResponseType::Basic:
response = response->BasicResponse();
@ -124,13 +131,17 @@ InternalResponseMetadata InternalResponse::GetMetadata() {
Maybe<mozilla::ipc::PrincipalInfo> principalInfo =
mPrincipalInfo ? Some(*mPrincipalInfo) : Nothing();
nsAutoCString bodyBlobURISpec(BodyBlobURISpec());
nsAutoString bodyLocalPath(BodyLocalPath());
// Note: all the arguments are copied rather than moved, which would be more
// efficient, because there's no move-friendly constructor generated.
nsCOMPtr<nsITransportSecurityInfo> securityInfo(mChannelInfo.SecurityInfo());
return InternalResponseMetadata(
mType, GetUnfilteredURLList(), GetUnfilteredStatus(),
GetUnfilteredStatusText(), headersGuard, headers, mErrorCode,
GetAlternativeDataType(), securityInfo, principalInfo);
GetAlternativeDataType(), securityInfo, principalInfo, bodyBlobURISpec,
bodyLocalPath, GetCredentialsMode());
}
void InternalResponse::ToChildToParentInternalResponse(
@ -196,15 +207,16 @@ ParentToChildInternalResponse InternalResponse::ToParentToChildInternalResponse(
GetUnfilteredBody(getter_AddRefs(body), &bodySize);
if (body) {
result.body() = Some(
ToParentToChildStream(WrapNotNull(body), bodySize, aBackgroundParent));
result.body() = Some(ToParentToChildStream(
WrapNotNull(body), bodySize, aBackgroundParent, mSerializeAsLazy));
result.bodySize() = bodySize;
}
nsCOMPtr<nsIInputStream> alternativeBody = TakeAlternativeBody();
if (alternativeBody) {
result.alternativeBody() = Some(ToParentToChildStream(
WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE, aBackgroundParent));
result.alternativeBody() = Some(
ToParentToChildStream(WrapNotNull(alternativeBody), UNKNOWN_BODY_SIZE,
aBackgroundParent, mSerializeAsLazy));
}
return result;

View File

@ -311,6 +311,9 @@ class InternalResponse final : public AtomicSafeRefCounted<InternalResponse> {
bool HasBeenCloned() const { return mCloned; }
void SetSerializeAsLazy(bool aAllow) { mSerializeAsLazy = aAllow; }
bool CanSerializeAsLazy() const { return mSerializeAsLazy; }
void InitChannelInfo(nsIChannel* aChannel) {
mChannelInfo.InitFromChannel(aChannel);
}
@ -342,6 +345,13 @@ class InternalResponse final : public AtomicSafeRefCounted<InternalResponse> {
InternalResponseMetadata GetMetadata();
RequestCredentials GetCredentialsMode() const {
if (mWrappedResponse) {
return mWrappedResponse->GetCredentialsMode();
}
return mCredentialsMode;
}
~InternalResponse();
private:
@ -380,6 +390,7 @@ class InternalResponse final : public AtomicSafeRefCounted<InternalResponse> {
nsCOMPtr<nsIInputStream> mAlternativeBody;
nsMainThreadPtrHandle<nsICacheInfoChannel> mCacheInfoChannel;
bool mCloned;
bool mSerializeAsLazy{true};
public:
static constexpr int64_t UNKNOWN_BODY_SIZE = -1;

View File

@ -160,7 +160,7 @@ ParentToParentFetchEventRespondWithResult ToParentToParent(
RemoteLazyInputStream::WrapStream(aBodyStream);
MOZ_DIAGNOSTIC_ASSERT(stream);
copyRequest.body().ref().get_ParentToChildStream().stream() = stream;
copyRequest.body().ref().get_ParentToChildStream() = stream;
}
Unused << aManager->SendPFetchEventOpProxyConstructor(actor, copyArgs);

View File

@ -1566,7 +1566,8 @@ RefPtr<FetchServicePromises> ServiceWorkerPrivate::SetupNavigationPreload(
MOZ_ALWAYS_SUCCEEDS(
aChannel->GetChannel(getter_AddRefs(underlyingChannel)));
RefPtr<FetchService> fetchService = FetchService::GetInstance();
return fetchService->Fetch(std::move(preloadRequest), underlyingChannel);
return fetchService->Fetch(AsVariant(FetchService::NavigationPreloadArgs{
std::move(preloadRequest), underlyingChannel}));
}
return FetchService::NetworkErrorResponse(NS_ERROR_UNEXPECTED);
}