Bug 1765777 - Resolve FetchEvent.preloadResponse when the response is available not the response end. r=dom-worker-reviewers,jesup.

In the previous implementation, FetchEvent.preloadResponse is resolved when the response fetching finishes.
However, ServiceWorker responding letency could be increased since waiting for preloadResponse finishes.
The patch resolves FetchEvent.preloadResponse earlier when the response is available.

The basic idea is to resolve the preload response when FetchInstance::OnResponseAvailableInternal() is called.
Then propagating the response from the parent process main thread to the content process worker thread. This is achieved by IPC PFetchEventOp::Send/RecvPreloadResponse -> PFetchEventOpProxy::Send/RecvPreloadResponse.

Since we can only get the response's ResourceTiming when FetchInstance::OnResponseEnd() is called. This patch introduces a new IPC method to propagate the ResourceTiming information from the parent process main thread to the content process worker thread.
PFetchEventOp::Send/RecvPreloadResponseEnd -> PFetchEventOpProxy->Send/RecvPreloadResponseEnd.

The tricky of this patch is we must extend the life cycle of FetchEventOp object if preloadResponse is set into FetchEvent.
That because ServiceWorker could resolve FetchEvent.respondWith() by using FetchEvent.preloadResponse.
In that case, FetchEventOp will get into a finish state and try to call FetchEventOpProxyChild::Senddelete with the operation result.
However, the ResponseEnd could not be called at the moment, and we need to wait for the corresponding timing information and its end reason.
To extend the life cycle of FetchEventOp, this patch cached the operation result while we get FetchEventOp::Callback is called. Then call FetchEventOpProxyChile::Senddelete() in FetchEventOpProxyChild::RecvPreloadResponseEnd() to close IPC. Or Senddelete() will be called while ActorDestroy() caused by shutdown.

Differential Revision: https://phabricator.services.mozilla.com/D145338
This commit is contained in:
Eden Chuang 2022-05-11 19:40:47 +00:00
parent f0e9b26ed1
commit f03fb58873
19 changed files with 448 additions and 249 deletions

View File

@ -12,6 +12,7 @@
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/FetchDriver.h"
namespace IPC {
template <>
@ -51,6 +52,12 @@ struct ParamTraits<mozilla::dom::ResponseType>
: public ContiguousEnumSerializer<mozilla::dom::ResponseType,
mozilla::dom::ResponseType::Basic,
mozilla::dom::ResponseType::EndGuard_> {};
template <>
struct ParamTraits<mozilla::dom::FetchDriverObserver::EndReason>
: public ContiguousEnumSerializerInclusive<
mozilla::dom::FetchDriverObserver::EndReason,
mozilla::dom::FetchDriverObserver::eAborted,
mozilla::dom::FetchDriverObserver::eByNetworking> {};
} // namespace IPC
#endif // mozilla_dom_fetch_IPCUtils_h

View File

@ -29,10 +29,52 @@ namespace mozilla::dom {
mozilla::LazyLogModule gFetchLog("Fetch");
FetchServiceResponse CreateErrorResponse(nsresult aRv) {
IPCPerformanceTimingData ipcTimingData;
return MakeTuple(InternalResponse::NetworkError(aRv), ipcTimingData,
EmptyString(), EmptyString());
// FetchServicePromises
FetchServicePromises::FetchServicePromises()
: mAvailablePromise(
new FetchServiceResponseAvailablePromise::Private(__func__)),
mEndPromise(new FetchServiceResponseEndPromise::Private(__func__)) {
mAvailablePromise->UseSynchronousTaskDispatch(__func__);
mEndPromise->UseSynchronousTaskDispatch(__func__);
}
RefPtr<FetchServiceResponseAvailablePromise>
FetchServicePromises::GetResponseAvailablePromise() {
return mAvailablePromise;
}
RefPtr<FetchServiceResponseEndPromise>
FetchServicePromises::GetResponseEndPromise() {
return mEndPromise;
}
void FetchServicePromises::ResolveResponseAvailablePromise(
FetchServiceResponse&& aResponse, const char* aMethodName) {
if (mAvailablePromise) {
mAvailablePromise->Resolve(std::move(aResponse), aMethodName);
}
}
void FetchServicePromises::RejectResponseAvailablePromise(
const CopyableErrorResult&& aError, const char* aMethodName) {
if (mAvailablePromise) {
mAvailablePromise->Reject(aError, aMethodName);
}
}
void FetchServicePromises::ResolveResponseEndPromise(ResponseEndArgs&& aArgs,
const char* aMethodName) {
if (mEndPromise) {
mEndPromise->Resolve(std::move(aArgs), aMethodName);
}
}
void FetchServicePromises::RejectResponseEndPromise(
const CopyableErrorResult&& aError, const char* aMethodName) {
if (mEndPromise) {
mEndPromise->Reject(aError, aMethodName);
}
}
// FetchInstance
@ -101,7 +143,7 @@ nsresult FetchService::FetchInstance::Initialize(nsIChannel* aChannel) {
return NS_OK;
}
RefPtr<FetchServiceResponsePromise> FetchService::FetchInstance::Fetch() {
RefPtr<FetchServicePromises> FetchService::FetchInstance::Fetch() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
@ -140,7 +182,9 @@ RefPtr<FetchServiceResponsePromise> FetchService::FetchInstance::Fetch() {
return FetchService::NetworkErrorResponse(rv);
}
return mResponsePromiseHolder.Ensure(__func__);
mPromises = MakeRefPtr<FetchServicePromises>();
return mPromises;
}
void FetchService::FetchInstance::Cancel() {
@ -153,8 +197,14 @@ void FetchService::FetchInstance::Cancel() {
mFetchDriver->RunAbortAlgorithm();
}
mResponsePromiseHolder.ResolveIfExists(
CreateErrorResponse(NS_ERROR_DOM_ABORT_ERR), __func__);
if (mPromises) {
mPromises->ResolveResponseAvailablePromise(
InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
mPromises->ResolveResponseEndPromise(
ResponseEndArgs(FetchDriverObserver::eAborted, Nothing()), __func__);
mPromises = nullptr;
}
}
void FetchService::FetchInstance::OnResponseEnd(
@ -162,47 +212,50 @@ void FetchService::FetchInstance::OnResponseEnd(
FETCH_LOG(("FetchInstance::OnResponseEnd [%p]", this));
if (aReason == eAborted) {
FETCH_LOG(("FetchInstance::OnResponseEnd end with eAborted"));
mResponsePromiseHolder.ResolveIfExists(
CreateErrorResponse(NS_ERROR_DOM_ABORT_ERR), __func__);
if (mPromises) {
mPromises->ResolveResponseEndPromise(
ResponseEndArgs(FetchDriverObserver::eAborted, Nothing()), __func__);
}
return;
}
if (!mResponsePromiseHolder.IsEmpty()) {
if (mPromises) {
// Remove the FetchInstance from FetchInstanceTable
RefPtr<FetchServiceResponsePromise> responsePromise =
mResponsePromiseHolder.Ensure(__func__);
RefPtr<FetchService> fetchService = FetchService::GetInstance();
MOZ_ASSERT(fetchService);
auto entry = fetchService->mFetchInstanceTable.Lookup(responsePromise);
auto entry = fetchService->mFetchInstanceTable.Lookup(mPromises);
MOZ_ASSERT(entry);
entry.Remove();
FETCH_LOG(
("FetchInstance::OnResponseEnd entry of responsePromise[%p] is removed",
responsePromise.get()));
("FetchInstance::OnResponseEnd entry[%p] of FetchInstance[%p] is "
"removed",
mPromises.get(), this));
// Get PerformanceTimingData from FetchDriver.
ResponseTiming timing;
UniquePtr<PerformanceTimingData> performanceTiming(
mFetchDriver->GetPerformanceTimingData(timing.initiatorType(),
timing.entryName()));
if (performanceTiming != nullptr) {
timing.timingData() = performanceTiming->ToIPC();
}
timing.initiatorType() = u"navigation"_ns;
// Resolve the ResponseEndPromise
mPromises->ResolveResponseEndPromise(ResponseEndArgs(aReason, Some(timing)),
__func__);
// Release promises
mPromises = nullptr;
}
// Get PerformanceTimingData from FetchDriver.
IPCPerformanceTimingData ipcPerformanceTiming;
nsString initiatorType;
nsString entryName;
UniquePtr<PerformanceTimingData> performanceTiming(
mFetchDriver->GetPerformanceTimingData(initiatorType, entryName));
if (performanceTiming != nullptr) {
ipcPerformanceTiming = performanceTiming->ToIPC();
}
initiatorType = u"navigation"_ns;
FetchServiceResponse response = MakeTuple(
std::move(mResponse), ipcPerformanceTiming, initiatorType, entryName);
// Resolve the FetchServiceResponsePromise
mResponsePromiseHolder.ResolveIfExists(std::move(response), __func__);
}
void FetchService::FetchInstance::OnResponseAvailableInternal(
SafeRefPtr<InternalResponse> aResponse) {
FETCH_LOG(("FetchInstance::OnResponseAvailableInternal [%p]", this));
mResponse = std::move(aResponse);
if (mPromises) {
// Resolve the ResponseAvailablePromise
mPromises->ResolveResponseAvailablePromise(std::move(aResponse), __func__);
}
}
// TODO:
@ -237,10 +290,13 @@ already_AddRefed<FetchService> FetchService::GetInstance() {
}
/*static*/
RefPtr<FetchServiceResponsePromise> FetchService::NetworkErrorResponse(
nsresult aRv) {
return FetchServiceResponsePromise::CreateAndResolve(CreateErrorResponse(aRv),
__func__);
RefPtr<FetchServicePromises> FetchService::NetworkErrorResponse(nsresult aRv) {
RefPtr<FetchServicePromises> promises = MakeRefPtr<FetchServicePromises>();
promises->ResolveResponseAvailablePromise(InternalResponse::NetworkError(aRv),
__func__);
promises->ResolveResponseEndPromise(
ResponseEndArgs(FetchDriverObserver::eAborted, Nothing()), __func__);
return promises;
}
FetchService::FetchService() {
@ -325,7 +381,7 @@ NS_IMETHODIMP FetchService::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
RefPtr<FetchServiceResponsePromise> FetchService::Fetch(
RefPtr<FetchServicePromises> FetchService::Fetch(
SafeRefPtr<InternalRequest> aRequest, nsIChannel* aChannel) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
@ -349,45 +405,42 @@ RefPtr<FetchServiceResponsePromise> FetchService::Fetch(
}
// Call FetchInstance::Fetch() to start an asynchronous fetching.
RefPtr<FetchServiceResponsePromise> responsePromise = fetch->Fetch();
RefPtr<FetchServicePromises> promises = fetch->Fetch();
if (!responsePromise->IsResolved()) {
if (!promises->GetResponseAvailablePromise()->IsResolved()) {
// Insert the created FetchInstance into FetchInstanceTable.
if (!mFetchInstanceTable.WithEntryHandle(responsePromise,
[&](auto&& entry) {
if (entry.HasEntry()) {
return false;
}
entry.Insert(fetch);
return true;
})) {
FETCH_LOG(("FetchService::Fetch entry of responsePromise[%p] exists",
responsePromise.get()));
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_LOG(("FetchService::Fetch responsePromise[%p], fetchInstance[%p]",
responsePromise.get(), fetch.get()));
FETCH_LOG(("FetchService::Fetch entry[%p] of FetchInstance[%p] added",
promises.get(), fetch.get()));
}
return responsePromise;
return promises;
}
void FetchService::CancelFetch(
RefPtr<FetchServiceResponsePromise>&& aResponsePromise) {
void FetchService::CancelFetch(RefPtr<FetchServicePromises>&& aPromises) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aResponsePromise);
FETCH_LOG(("FetchService::CancelFetch aResponsePromise[%p]",
aResponsePromise.get()));
MOZ_ASSERT(aPromises);
FETCH_LOG(("FetchService::CancelFetch aPromises[%p]", aPromises.get()));
auto entry = mFetchInstanceTable.Lookup(aResponsePromise);
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();
entry.Remove();
FETCH_LOG(("FetchService::CancelFetch aResponsePromise[%p] is removed",
aResponsePromise.get()));
FETCH_LOG(
("FetchService::CancelFetch entry [%p] removed", aPromises.get()));
}
}

View File

@ -11,6 +11,7 @@
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/FetchDriver.h"
#include "mozilla/dom/FetchTypes.h"
#include "mozilla/dom/PerformanceTimingTypes.h"
#include "mozilla/dom/SafeRefPtr.h"
@ -24,12 +25,36 @@ namespace mozilla::dom {
class InternalRequest;
class InternalResponse;
using FetchServiceResponse =
Tuple<SafeRefPtr<InternalResponse>, IPCPerformanceTimingData, nsString,
nsString>;
using FetchServiceResponsePromise =
using FetchServiceResponse = SafeRefPtr<InternalResponse>;
using FetchServiceResponseAvailablePromise =
MozPromise<FetchServiceResponse, CopyableErrorResult, true>;
using FetchServiceResponseEndPromise =
MozPromise<ResponseEndArgs, CopyableErrorResult, true>;
class FetchServicePromises final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchServicePromises);
public:
FetchServicePromises();
RefPtr<FetchServiceResponseAvailablePromise> GetResponseAvailablePromise();
RefPtr<FetchServiceResponseEndPromise> GetResponseEndPromise();
void ResolveResponseAvailablePromise(FetchServiceResponse&& aResponse,
const char* aMethodName);
void RejectResponseAvailablePromise(const CopyableErrorResult&& aError,
const char* aMethodName);
void ResolveResponseEndPromise(ResponseEndArgs&& aArgs,
const char* aMethodName);
void RejectResponseEndPromise(const CopyableErrorResult&& aError,
const char* aMethodName);
private:
~FetchServicePromises() = default;
RefPtr<FetchServiceResponseAvailablePromise::Private> mAvailablePromise;
RefPtr<FetchServiceResponseEndPromise::Private> mEndPromise;
};
/**
* FetchService is a singleton object which designed to be used in parent
@ -37,9 +62,9 @@ using FetchServiceResponsePromise =
* from ServiceWorkers(by Navigation Preload) and PFetch.
*
* FetchService creates FetchInstance internally to represent each Fetch
* request. It supports an asynchronous fetching, FetchServiceResponsePromise is
* request. It supports an asynchronous fetching, FetchServicePromises is
* created when a Fetch starts, once the response is ready or any error happens,
* the FetchServiceResponsePromise would be resolved or rejected. The promise
* the FetchServicePromises would be resolved or rejected. The promises
* consumers can set callbacks to handle the Fetch result.
*/
class FetchService final : public nsIObserver {
@ -49,29 +74,29 @@ class FetchService final : public nsIObserver {
static already_AddRefed<FetchService> GetInstance();
static RefPtr<FetchServiceResponsePromise> NetworkErrorResponse(nsresult aRv);
static RefPtr<FetchServicePromises> 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);
RefPtr<FetchServicePromises> Fetch(SafeRefPtr<InternalRequest> aRequest,
nsIChannel* aChannel = nullptr);
void CancelFetch(RefPtr<FetchServiceResponsePromise>&& aResponsePromise);
void CancelFetch(RefPtr<FetchServicePromises>&& aPromises);
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.
* resolve/reject the FetchServicePromises.
* 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();
* RefPtr<FetchServicePromises> fetch->Fetch();
*/
class FetchInstance final : public FetchDriverObserver {
public:
@ -86,7 +111,7 @@ class FetchService final : public nsIObserver {
// mRequest
nsresult Initialize(nsIChannel* aChannel = nullptr);
RefPtr<FetchServiceResponsePromise> Fetch();
RefPtr<FetchServicePromises> Fetch();
void Cancel();
@ -109,7 +134,7 @@ class FetchService final : public nsIObserver {
RefPtr<FetchDriver> mFetchDriver;
SafeRefPtr<InternalResponse> mResponse;
MozPromiseHolder<FetchServiceResponsePromise> mResponsePromiseHolder;
RefPtr<FetchServicePromises> mPromises;
};
~FetchService();
@ -118,8 +143,7 @@ class FetchService final : public nsIObserver {
nsresult UnregisterNetworkObserver();
// This is a container to manage the generated fetches.
nsTHashMap<nsRefPtrHashKey<FetchServiceResponsePromise>,
RefPtr<FetchInstance> >
nsTHashMap<nsRefPtrHashKey<FetchServicePromises>, RefPtr<FetchInstance> >
mFetchInstanceTable;
bool mObservingNetwork{false};
bool mOffline{false};

View File

@ -19,6 +19,7 @@ using RequestMode from "mozilla/dom/RequestBinding.h";
using RequestRedirect from "mozilla/dom/RequestBinding.h";
using ResponseType from "mozilla/dom/ResponseBinding.h";
using struct nsID from "nsID.h";
using FetchDriverObserver::EndReason from "mozilla/dom/FetchDriver.h";
namespace mozilla {
namespace dom {
@ -101,18 +102,15 @@ struct ChildToParentInternalResponse {
ChildToParentStream? alternativeBody;
};
struct ParentToParentResponseWithTiming {
ParentToParentInternalResponse response;
struct ResponseTiming {
IPCPerformanceTimingData timingData;
nsString initiatorType;
nsString entryName;
};
struct ParentToChildResponseWithTiming {
ParentToChildInternalResponse response;
IPCPerformanceTimingData timingData;
nsString initiatorType;
nsString entryName;
struct ResponseEndArgs {
EndReason endReason;
ResponseTiming? timing;
};
} // namespace ipc

View File

@ -184,7 +184,7 @@ NS_IMPL_ISUPPORTS(SynthesizeResponseWatcher, nsIInterceptedBodyCallback)
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel> aInterceptedChannel,
RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise,
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises,
RefPtr<KeepAliveToken>&& aKeepAliveToken) {
AssertIsOnMainThread();
MOZ_ASSERT(aManager);
@ -193,7 +193,7 @@ NS_IMPL_ISUPPORTS(SynthesizeResponseWatcher, nsIInterceptedBodyCallback)
FetchEventOpChild* actor = new FetchEventOpChild(
std::move(aArgs), std::move(aInterceptedChannel),
std::move(aRegistration), std::move(aPreloadResponseReadyPromise),
std::move(aRegistration), std::move(aPreloadResponseReadyPromises),
std::move(aKeepAliveToken));
actor->mWasSent = true;
@ -212,53 +212,63 @@ FetchEventOpChild::FetchEventOpChild(
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel>&& aInterceptedChannel,
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise,
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises,
RefPtr<KeepAliveToken>&& aKeepAliveToken)
: mArgs(std::move(aArgs)),
mInterceptedChannel(std::move(aInterceptedChannel)),
mRegistration(std::move(aRegistration)),
mKeepAliveToken(std::move(aKeepAliveToken)),
mPreloadResponseReadyPromise(std::move(aPreloadResponseReadyPromise)) {
if (mPreloadResponseReadyPromise) {
mPreloadResponseReadyPromises(std::move(aPreloadResponseReadyPromises)) {
if (mPreloadResponseReadyPromises) {
// This promise should be configured to use synchronous dispatch, so if it's
// already resolved when we run this code then the callback will be called
// synchronously and pass the preload response with the constructor message.
//
// Note that it's fine to capture the this pointer in the callbacks because
// we disconnect the request in Recv__delete__().
mPreloadResponseReadyPromise
mPreloadResponseReadyPromises->GetResponseAvailablePromise()
->Then(
GetCurrentSerialEventTarget(), __func__,
[this](FetchServiceResponse&& aResponse) {
SafeRefPtr<InternalResponse> preloadResponse;
IPCPerformanceTimingData timingData;
nsString initiatorType;
nsString entryName;
Tie(preloadResponse, timingData, initiatorType, entryName) =
std::move(aResponse);
ParentToParentResponseWithTiming response;
response.response() =
preloadResponse->ToParentToParentInternalResponse();
response.timingData() = timingData;
response.initiatorType() = initiatorType;
response.entryName() = entryName;
if (!mWasSent) {
// The actor wasn't sent yet, we can still send the preload
// response with it.
mArgs.preloadResponse() = Some(std::move(response));
mArgs.preloadResponse() =
Some(aResponse->ToParentToParentInternalResponse());
} else {
// It's too late to send the preload response with the actor, we
// have to send it in a separate message.
SendPreloadResponse(response);
SendPreloadResponse(
aResponse->ToParentToParentInternalResponse());
}
mPreloadResponseReadyPromise = nullptr;
mPreloadResponseReadyPromiseRequestHolder.Complete();
mPreloadResponseAvailablePromiseRequestHolder.Complete();
},
[this](const CopyableErrorResult&) {
mPreloadResponseReadyPromise = nullptr;
mPreloadResponseReadyPromiseRequestHolder.Complete();
mPreloadResponseAvailablePromiseRequestHolder.Complete();
})
->Track(mPreloadResponseReadyPromiseRequestHolder);
->Track(mPreloadResponseAvailablePromiseRequestHolder);
mPreloadResponseReadyPromises->GetResponseEndPromise()
->Then(
GetCurrentSerialEventTarget(), __func__,
[this](ResponseEndArgs&& aResponse) {
if (!mWasSent) {
// The actor wasn't sent yet, we can still send the preload
// response end args with it.
mArgs.preloadResponseEndArgs() = Some(std::move(aResponse));
} else {
// It's too late to send the preload response end with the
// actor, we have to send it in a separate message.
SendPreloadResponseEnd(aResponse);
}
mPreloadResponseReadyPromises = nullptr;
mPreloadResponseEndPromiseRequestHolder.Complete();
},
[this](const CopyableErrorResult&) {
mPreloadResponseReadyPromises = nullptr;
mPreloadResponseEndPromiseRequestHolder.Complete();
})
->Track(mPreloadResponseEndPromiseRequestHolder);
}
}
@ -279,14 +289,6 @@ mozilla::ipc::IPCResult FetchEventOpChild::RecvRespondWith(
ParentToParentFetchEventRespondWithResult&& aResult) {
AssertIsOnMainThread();
// Preload response is too late to be ready after we receive RespondWith, so
// disconnect the promise.
mPreloadResponseReadyPromiseRequestHolder.DisconnectIfExists();
if (mPreloadResponseReadyPromise) {
RefPtr<FetchService> fetchService = FetchService::GetInstance();
fetchService->CancelFetch(std::move(mPreloadResponseReadyPromise));
}
switch (aResult.type()) {
case ParentToParentFetchEventRespondWithResult::
TParentToParentSynthesizeResponseArgs:
@ -326,6 +328,22 @@ mozilla::ipc::IPCResult FetchEventOpChild::RecvRespondWith(
break;
}
// Preload response is too late to be ready after we receive RespondWith, so
// disconnect the promise.
// Notice that if mPreloadResponseAvailablePromiseRequestHolder does not
// exist. We don't disconnect mPreloadResponseEndPromiseRequestHolder and
// cancel the navigation preload fetch here. Since ServiceWorker script could
// call FetchEvent.respondWith(FetchEvent.preloadResponse), then we get here
// but the navigation preload is not yet finished.
if (mPreloadResponseAvailablePromiseRequestHolder.Exists()) {
mPreloadResponseAvailablePromiseRequestHolder.Disconnect();
mPreloadResponseEndPromiseRequestHolder.Disconnect();
if (mPreloadResponseReadyPromises) {
RefPtr<FetchService> fetchService = FetchService::GetInstance();
fetchService->CancelFetch(std::move(mPreloadResponseReadyPromises));
}
}
return IPC_OK();
}
@ -344,10 +362,11 @@ mozilla::ipc::IPCResult FetchEventOpChild::Recv__delete__(
}
mPromiseHolder.ResolveIfExists(true, __func__);
mPreloadResponseReadyPromiseRequestHolder.DisconnectIfExists();
if (mPreloadResponseReadyPromise) {
mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists();
mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
if (mPreloadResponseReadyPromises) {
RefPtr<FetchService> fetchService = FetchService::GetInstance();
fetchService->CancelFetch(std::move(mPreloadResponseReadyPromise));
fetchService->CancelFetch(std::move(mPreloadResponseReadyPromises));
}
/**

View File

@ -35,7 +35,7 @@ class FetchEventOpChild final : public PFetchEventOpChild {
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel> aInterceptedChannel,
RefPtr<ServiceWorkerRegistrationInfo> aRegistrationInfo,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise,
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises,
RefPtr<KeepAliveToken>&& aKeepAliveToken);
~FetchEventOpChild();
@ -45,7 +45,7 @@ class FetchEventOpChild final : public PFetchEventOpChild {
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel>&& aInterceptedChannel,
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistrationInfo,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise,
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises,
RefPtr<KeepAliveToken>&& aKeepAliveToken);
mozilla::ipc::IPCResult RecvAsyncLog(const nsCString& aScriptSpec,
@ -80,9 +80,11 @@ class FetchEventOpChild final : public PFetchEventOpChild {
bool mInterceptedChannelHandled = false;
MozPromiseHolder<GenericPromise> mPromiseHolder;
bool mWasSent = false;
MozPromiseRequestHolder<FetchServiceResponsePromise>
mPreloadResponseReadyPromiseRequestHolder;
RefPtr<FetchServiceResponsePromise> mPreloadResponseReadyPromise;
MozPromiseRequestHolder<FetchServiceResponseAvailablePromise>
mPreloadResponseAvailablePromiseRequestHolder;
MozPromiseRequestHolder<FetchServiceResponseEndPromise>
mPreloadResponseEndPromiseRequestHolder;
RefPtr<FetchServicePromises> mPreloadResponseReadyPromises;
};
} // namespace mozilla::dom

View File

@ -25,12 +25,15 @@ using namespace ipc;
namespace dom {
Maybe<ParentToParentResponseWithTiming> FetchEventOpParent::OnStart(
Tuple<Maybe<ParentToParentInternalResponse>, Maybe<ResponseEndArgs>>
FetchEventOpParent::OnStart(
MovingNotNull<RefPtr<FetchEventOpProxyParent>> aFetchEventOpProxyParent) {
Maybe<ParentToParentResponseWithTiming> preloadResponse =
Maybe<ParentToParentInternalResponse> preloadResponse =
std::move(mState.as<Pending>().mPreloadResponse);
Maybe<ResponseEndArgs> preloadResponseEndArgs =
std::move(mState.as<Pending>().mEndArgs);
mState = AsVariant(Started{std::move(aFetchEventOpProxyParent)});
return preloadResponse;
return MakeTuple(preloadResponse, preloadResponseEndArgs);
}
void FetchEventOpParent::OnFinish() {
@ -39,7 +42,7 @@ void FetchEventOpParent::OnFinish() {
}
mozilla::ipc::IPCResult FetchEventOpParent::RecvPreloadResponse(
ParentToParentResponseWithTiming&& aResponse) {
ParentToParentInternalResponse&& aResponse) {
AssertIsOnBackgroundThread();
mState.match(
@ -48,17 +51,29 @@ mozilla::ipc::IPCResult FetchEventOpParent::RecvPreloadResponse(
aPending.mPreloadResponse = Some(std::move(aResponse));
},
[&aResponse](Started& aStarted) {
ParentToChildResponseWithTiming response;
auto backgroundParent = WrapNotNull(
WrapNotNull(aStarted.mFetchEventOpProxyParent->Manager())
->Manager());
response.response() =
ToParentToChild(aResponse.response(), backgroundParent);
response.timingData() = aResponse.timingData();
response.initiatorType() = aResponse.initiatorType();
response.entryName() = aResponse.entryName();
Unused << aStarted.mFetchEventOpProxyParent->SendPreloadResponse(
response);
ToParentToChild(aResponse, backgroundParent));
},
[](const Finished&) {});
return IPC_OK();
}
mozilla::ipc::IPCResult FetchEventOpParent::RecvPreloadResponseEnd(
ResponseEndArgs&& aArgs) {
AssertIsOnBackgroundThread();
mState.match(
[&aArgs](Pending& aPending) {
MOZ_ASSERT(aPending.mEndArgs.isNothing());
aPending.mEndArgs = Some(std::move(aArgs));
},
[&aArgs](Started& aStarted) {
Unused << aStarted.mFetchEventOpProxyParent->SendPreloadResponseEnd(
std::move(aArgs));
},
[](const Finished&) {});

View File

@ -9,6 +9,7 @@
#include "nsISupports.h"
#include "mozilla/Tuple.h"
#include "mozilla/dom/FetchEventOpProxyParent.h"
#include "mozilla/dom/PFetchEventOpParent.h"
@ -23,8 +24,8 @@ class FetchEventOpParent final : public PFetchEventOpParent {
FetchEventOpParent() = default;
// Transition from the Pending state to the Started state. Returns the preload
// response, if it has already arrived.
Maybe<ParentToParentResponseWithTiming> OnStart(
// response and response end args, if it has already arrived.
Tuple<Maybe<ParentToParentInternalResponse>, Maybe<ResponseEndArgs>> OnStart(
MovingNotNull<RefPtr<FetchEventOpProxyParent>> aFetchEventOpProxyParent);
// Transition from the Started state to the Finished state.
@ -36,12 +37,15 @@ class FetchEventOpParent final : public PFetchEventOpParent {
// IPDL methods
mozilla::ipc::IPCResult RecvPreloadResponse(
ParentToParentResponseWithTiming&& aResponse);
ParentToParentInternalResponse&& aResponse);
mozilla::ipc::IPCResult RecvPreloadResponseEnd(ResponseEndArgs&& aArgs);
void ActorDestroy(ActorDestroyReason) override;
struct Pending {
Maybe<ParentToParentResponseWithTiming> mPreloadResponse;
Maybe<ParentToParentInternalResponse> mPreloadResponse;
Maybe<ResponseEndArgs> mEndArgs;
};
struct Started {

View File

@ -75,17 +75,21 @@ void FetchEventOpProxyChild::Initialize(
// We use synchronous task dispatch here to make sure that if the preload
// response arrived before we dispatch the fetch event, then the JS preload
// response promise will get resolved immediately.
mPreloadResponsePromise =
MakeRefPtr<FetchEventPreloadResponsePromise::Private>(__func__);
mPreloadResponsePromise->UseSynchronousTaskDispatch(__func__);
if (aArgs.preloadResponse().isSome()) {
FetchEventPreloadResponseArgs response = MakeTuple(
InternalResponse::FromIPC(aArgs.preloadResponse().ref().response()),
aArgs.preloadResponse().ref().timingData(),
aArgs.preloadResponse().ref().initiatorType(),
aArgs.preloadResponse().ref().entryName());
mPreloadResponseAvailablePromise =
MakeRefPtr<FetchEventPreloadResponseAvailablePromise::Private>(
__func__);
mPreloadResponseAvailablePromise->UseSynchronousTaskDispatch(__func__);
mPreloadResponsePromise->Resolve(std::move(response), __func__);
if (aArgs.preloadResponse().isSome()) {
mPreloadResponseAvailablePromise->Resolve(
InternalResponse::FromIPC(aArgs.preloadResponse().ref()), __func__);
}
mPreloadResponseEndPromise =
MakeRefPtr<FetchEventPreloadResponseEndPromise::Private>(__func__);
mPreloadResponseEndPromise->UseSynchronousTaskDispatch(__func__);
if (aArgs.preloadResponseEndArgs().isSome()) {
mPreloadResponseEndPromise->Resolve(aArgs.preloadResponseEndArgs().ref(),
__func__);
}
}
@ -95,6 +99,16 @@ void FetchEventOpProxyChild::Initialize(
RefPtr<FetchEventOpProxyChild> self = this;
auto callback = [self](const ServiceWorkerOpResult& aResult) {
// FetchEventOp could finish before NavigationPreload fetch finishes.
// If NavigationPreload is available in FetchEvent, caching FetchEventOp
// result until RecvPreloadResponseEnd is called, such that the response's
// ResourceTiming could be recorded in worker's performanceStorage.
if (self->mPreloadResponseEndPromise &&
!self->mPreloadResponseEndPromise->IsResolved() &&
self->mPreloadResponseAvailablePromise->IsResolved()) {
self->mCachedOpResult = Some(aResult);
return;
}
if (!self->CanSend()) {
return;
}
@ -178,22 +192,55 @@ SafeRefPtr<InternalRequest> FetchEventOpProxyChild::ExtractInternalRequest() {
return std::move(mInternalRequest);
}
RefPtr<FetchEventPreloadResponsePromise>
FetchEventOpProxyChild::GetPreloadResponsePromise() {
return mPreloadResponsePromise;
RefPtr<FetchEventPreloadResponseAvailablePromise>
FetchEventOpProxyChild::GetPreloadResponseAvailablePromise() {
return mPreloadResponseAvailablePromise;
}
RefPtr<FetchEventPreloadResponseEndPromise>
FetchEventOpProxyChild::GetPreloadResponseEndPromise() {
return mPreloadResponseEndPromise;
}
mozilla::ipc::IPCResult FetchEventOpProxyChild::RecvPreloadResponse(
ParentToChildResponseWithTiming&& aResponse) {
ParentToChildInternalResponse&& aResponse) {
// Receiving this message implies that navigation preload is enabled, so
// Initialize() should have created this promise.
MOZ_ASSERT(mPreloadResponsePromise);
MOZ_ASSERT(mPreloadResponseAvailablePromise);
FetchEventPreloadResponseArgs response = MakeTuple(
InternalResponse::FromIPC(aResponse.response()), aResponse.timingData(),
aResponse.initiatorType(), aResponse.entryName());
mPreloadResponseAvailablePromise->Resolve(
InternalResponse::FromIPC(aResponse), __func__);
mPreloadResponsePromise->Resolve(std::move(response), __func__);
return IPC_OK();
}
mozilla::ipc::IPCResult FetchEventOpProxyChild::RecvPreloadResponseEnd(
ResponseEndArgs&& aArgs) {
// Receiving this message implies that navigation preload is enabled, so
// Initialize() should have created this promise.
MOZ_ASSERT(mPreloadResponseEndPromise);
mPreloadResponseEndPromise->Resolve(std::move(aArgs), __func__);
// If mCachedOpResult is not nothing, it means FetchEventOp had already done
// and the operation result is cached. Continue closing IPC here.
if (mCachedOpResult.isNothing()) {
return IPC_OK();
}
if (!CanSend()) {
return IPC_OK();
}
if (NS_WARN_IF(mCachedOpResult.ref().type() ==
ServiceWorkerOpResult::Tnsresult)) {
Unused << Send__delete__(this, mCachedOpResult.ref().get_nsresult());
return IPC_OK();
}
MOZ_ASSERT(mCachedOpResult.ref().type() ==
ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult);
Unused << Send__delete__(this, mCachedOpResult.ref());
return IPC_OK();
}
@ -202,16 +249,18 @@ void FetchEventOpProxyChild::ActorDestroy(ActorDestroyReason) {
Unused << NS_WARN_IF(mRespondWithPromiseRequestHolder.Exists());
mRespondWithPromiseRequestHolder.DisconnectIfExists();
// If mPreloadResponsePromise exists, navigation preloading response will not
// be valid anymore since it is too late to respond to the FetchEvent.
// Resolve the preload response promise with NS_ERROR_DOM_ABORT_ERR.
if (mPreloadResponsePromise) {
IPCPerformanceTimingData timingData;
FetchEventPreloadResponseArgs response =
MakeTuple(InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR),
timingData, EmptyString(), EmptyString());
// If mPreloadResponseAvailablePromise exists, navigation preloading response
// will not be valid anymore since it is too late to respond to the
// FetchEvent. Resolve the preload response promise with
// NS_ERROR_DOM_ABORT_ERR.
if (mPreloadResponseAvailablePromise) {
mPreloadResponseAvailablePromise->Resolve(
InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__);
}
mPreloadResponsePromise->Resolve(std::move(response), __func__);
if (mPreloadResponseEndPromise) {
ResponseEndArgs args(FetchDriverObserver::eAborted, Nothing());
mPreloadResponseEndPromise->Resolve(args, __func__);
}
mOp->RevokeActor(this);

View File

@ -35,13 +35,18 @@ class FetchEventOpProxyChild final : public PFetchEventOpProxyChild {
// Must only be called once and on a worker thread.
SafeRefPtr<InternalRequest> ExtractInternalRequest();
RefPtr<FetchEventPreloadResponsePromise> GetPreloadResponsePromise();
RefPtr<FetchEventPreloadResponseAvailablePromise>
GetPreloadResponseAvailablePromise();
RefPtr<FetchEventPreloadResponseEndPromise> GetPreloadResponseEndPromise();
private:
~FetchEventOpProxyChild() = default;
mozilla::ipc::IPCResult RecvPreloadResponse(
ParentToChildResponseWithTiming&& aResponse);
ParentToChildInternalResponse&& aResponse);
mozilla::ipc::IPCResult RecvPreloadResponseEnd(ResponseEndArgs&& aArgs);
void ActorDestroy(ActorDestroyReason) override;
@ -53,7 +58,12 @@ class FetchEventOpProxyChild final : public PFetchEventOpProxyChild {
// Initialized on RemoteWorkerService::Thread, read on a worker thread.
SafeRefPtr<InternalRequest> mInternalRequest;
RefPtr<FetchEventPreloadResponsePromise::Private> mPreloadResponsePromise;
RefPtr<FetchEventPreloadResponseAvailablePromise::Private>
mPreloadResponseAvailablePromise;
RefPtr<FetchEventPreloadResponseEndPromise::Private>
mPreloadResponseEndPromise;
Maybe<ServiceWorkerOpResult> mCachedOpResult;
};
} // namespace mozilla::dom

View File

@ -118,18 +118,16 @@ ParentToParentFetchEventRespondWithResult ToParentToParent(
MOZ_ASSERT(aManager);
MOZ_ASSERT(aReal);
ParentToChildServiceWorkerFetchEventOpArgs copyArgs(aArgs.common(),
ParentToChildServiceWorkerFetchEventOpArgs copyArgs(aArgs.common(), Nothing(),
Nothing());
if (aArgs.preloadResponse().isSome()) {
// Convert the preload response to ParentToChildResponseWithTiming.
ParentToChildResponseWithTiming response;
response.response() =
ToParentToChild(aArgs.preloadResponse().ref().response(),
WrapNotNull(aManager->Manager()));
response.timingData() = aArgs.preloadResponse().ref().timingData();
response.initiatorType() = aArgs.preloadResponse().ref().initiatorType();
response.entryName() = aArgs.preloadResponse().ref().entryName();
copyArgs.preloadResponse() = Some(response);
// Convert the preload response to ParentToChildInternalResponse.
copyArgs.preloadResponse() = Some(ToParentToChild(
aArgs.preloadResponse().ref(), WrapNotNull(aManager->Manager())));
}
if (aArgs.preloadResponseEndArgs().isSome()) {
copyArgs.preloadResponseEndArgs() = aArgs.preloadResponseEndArgs();
}
FetchEventOpProxyParent* actor =
@ -141,16 +139,17 @@ ParentToParentFetchEventRespondWithResult ToParentToParent(
// need to add it to the arguments. Note that we have to make sure that the
// arguments don't contain the preload response already, otherwise we'll end
// up overwriting it with a Nothing.
Maybe<ParentToParentResponseWithTiming> preloadResponse =
Maybe<ParentToParentInternalResponse> preloadResponse;
Maybe<ResponseEndArgs> preloadResponseEndArgs;
Tie(preloadResponse, preloadResponseEndArgs) =
actor->mReal->OnStart(WrapNotNull(actor));
if (copyArgs.preloadResponse().isNothing() && preloadResponse.isSome()) {
ParentToChildResponseWithTiming response;
response.response() = ToParentToChild(preloadResponse.ref().response(),
WrapNotNull(aManager->Manager()));
response.timingData() = preloadResponse.ref().timingData();
response.initiatorType() = preloadResponse.ref().initiatorType();
response.entryName() = preloadResponse.ref().entryName();
copyArgs.preloadResponse() = Some(response);
copyArgs.preloadResponse() = Some(ToParentToChild(
preloadResponse.ref(), WrapNotNull(aManager->Manager())));
}
if (copyArgs.preloadResponseEndArgs().isNothing() &&
preloadResponseEndArgs.isSome()) {
copyArgs.preloadResponseEndArgs() = preloadResponseEndArgs;
}
IPCInternalRequest& copyRequest = copyArgs.common().internalRequest();
@ -202,7 +201,6 @@ mozilla::ipc::IPCResult FetchEventOpProxyParent::RecvRespondWith(
auto manager = WrapNotNull(mReal->Manager());
auto backgroundParent = WrapNotNull(manager->Manager());
Unused << mReal->SendRespondWith(ToParentToParent(aResult, backgroundParent));
mReal->OnFinish();
return IPC_OK();
}
@ -211,7 +209,7 @@ mozilla::ipc::IPCResult FetchEventOpProxyParent::Recv__delete__(
AssertIsOnBackgroundThread();
MOZ_ASSERT(mLifetimePromise);
MOZ_ASSERT(mReal);
mReal->OnFinish();
if (mLifetimePromise) {
mLifetimePromise->Resolve(aResult, __func__);
mLifetimePromise = nullptr;

View File

@ -15,7 +15,9 @@ protocol PFetchEventOp {
manager PRemoteWorkerController;
parent:
async PreloadResponse(ParentToParentResponseWithTiming aResponse);
async PreloadResponse(ParentToParentInternalResponse aResponse);
async PreloadResponseEnd(ResponseEndArgs aArgs);
child:
async AsyncLog(nsCString aScriptSpec, uint32_t aLineNumber,

View File

@ -23,7 +23,9 @@ protocol PFetchEventOpProxy {
async __delete__(ServiceWorkerFetchEventOpResult aResult);
child:
async PreloadResponse(ParentToChildResponseWithTiming aResponse);
async PreloadResponse(ParentToChildInternalResponse aResponse);
async PreloadResponseEnd(ResponseEndArgs aArgs);
};
} // namespace dom

View File

@ -1270,7 +1270,8 @@ void FetchEventOp::MaybeFinished() {
mHandled = nullptr;
mPreloadResponse = nullptr;
mPreloadResponsePromiseRequestHolder.DisconnectIfExists();
mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists();
mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
ServiceWorkerFetchEventOpResult result(
mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
@ -1672,42 +1673,55 @@ nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
mPreloadResponse = fetchEvent->PreloadResponse();
if (args.common().preloadNavigation()) {
RefPtr<FetchEventPreloadResponsePromise> preloadResponsePromise =
mActor->GetPreloadResponsePromise();
RefPtr<FetchEventPreloadResponseAvailablePromise> preloadResponsePromise =
mActor->GetPreloadResponseAvailablePromise();
MOZ_ASSERT(preloadResponsePromise);
// If preloadResponsePromise has already settled then this callback will get
// run synchronously here.
RefPtr<FetchEventOp> self = this;
RefPtr<PerformanceStorage> performanceStorage =
aWorkerPrivate->GetPerformanceStorage();
preloadResponsePromise
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, performanceStorage,
globalObjectAsSupports = std::move(globalObjectAsSupports)](
FetchEventPreloadResponseArgs&& aArgs) {
SafeRefPtr<InternalResponse> preloadResponse;
IPCPerformanceTimingData timingData;
nsString initiatorType;
nsString entryName;
Tie(preloadResponse, timingData, initiatorType, entryName) =
std::move(aArgs);
if (performanceStorage &&
NS_SUCCEEDED(preloadResponse->GetErrorCode())) {
performanceStorage->AddEntry(
entryName, initiatorType,
MakeUnique<PerformanceTimingData>(timingData));
}
self->mPreloadResponse->MaybeResolve(MakeRefPtr<Response>(
globalObjectAsSupports, std::move(preloadResponse), nullptr));
self->mPreloadResponsePromiseRequestHolder.Complete();
[self, globalObjectAsSupports](
SafeRefPtr<InternalResponse>&& aPreloadResponse) {
self->mPreloadResponse->MaybeResolve(
MakeRefPtr<Response>(globalObjectAsSupports,
std::move(aPreloadResponse), nullptr));
self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
},
[self](int) {
self->mPreloadResponsePromiseRequestHolder.Complete();
self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
})
->Track(mPreloadResponsePromiseRequestHolder);
->Track(mPreloadResponseAvailablePromiseRequestHolder);
RefPtr<PerformanceStorage> performanceStorage =
aWorkerPrivate->GetPerformanceStorage();
RefPtr<FetchEventPreloadResponseEndPromise> preloadResponseEndPromise =
mActor->GetPreloadResponseEndPromise();
MOZ_ASSERT(preloadResponseEndPromise);
preloadResponseEndPromise
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, performanceStorage,
globalObjectAsSupports](ResponseEndArgs&& aArgs) {
if (aArgs.timing().isSome() && performanceStorage) {
performanceStorage->AddEntry(
aArgs.timing().ref().entryName(),
aArgs.timing().ref().initiatorType(),
MakeUnique<PerformanceTimingData>(
aArgs.timing().ref().timingData()));
}
if (aArgs.endReason() == FetchDriverObserver::eAborted) {
self->mPreloadResponse->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
self->mPreloadResponseEndPromiseRequestHolder.Complete();
},
[self](int) {
self->mPreloadResponseEndPromiseRequestHolder.Complete();
})
->Track(mPreloadResponseEndPromiseRequestHolder);
} else {
// preload navigation is disabled, resolved preload response promise with
// undefined as default behavior.

View File

@ -181,8 +181,10 @@ class FetchEventOp final : public ExtendableEventOp,
RefPtr<Promise> mPreloadResponse;
// Holds the callback that resolves mPreloadResponse.
MozPromiseRequestHolder<FetchEventPreloadResponsePromise>
mPreloadResponsePromiseRequestHolder;
MozPromiseRequestHolder<FetchEventPreloadResponseAvailablePromise>
mPreloadResponseAvailablePromiseRequestHolder;
MozPromiseRequestHolder<FetchEventPreloadResponseEndPromise>
mPreloadResponseEndPromiseRequestHolder;
TimeStamp mFetchHandlerStart;
TimeStamp mFetchHandlerFinish;

View File

@ -93,12 +93,14 @@ struct ServiceWorkerFetchEventOpArgsCommon {
struct ParentToParentServiceWorkerFetchEventOpArgs {
ServiceWorkerFetchEventOpArgsCommon common;
ParentToParentResponseWithTiming? preloadResponse;
ParentToParentInternalResponse? preloadResponse;
ResponseEndArgs? preloadResponseEndArgs;
};
struct ParentToChildServiceWorkerFetchEventOpArgs {
ServiceWorkerFetchEventOpArgsCommon common;
ParentToChildResponseWithTiming? preloadResponse;
ParentToChildInternalResponse? preloadResponse;
ResponseEndArgs? preloadResponseEndArgs;
};
union ServiceWorkerOpArgs {

View File

@ -29,14 +29,13 @@ using FetchEventRespondWithResult =
using FetchEventRespondWithPromise =
MozPromise<FetchEventRespondWithResult, CancelInterceptionArgs, true>;
using FetchEventPreloadResponseArgs =
Tuple<SafeRefPtr<InternalResponse>, IPCPerformanceTimingData, nsString,
nsString>;
// The reject type int is arbitrary, since this promise will never get rejected.
// Unfortunately void is not supported as a reject type.
using FetchEventPreloadResponsePromise =
MozPromise<FetchEventPreloadResponseArgs, int, true>;
using FetchEventPreloadResponseAvailablePromise =
MozPromise<SafeRefPtr<InternalResponse>, int, true>;
using FetchEventPreloadResponseEndPromise =
MozPromise<ResponseEndArgs, int, true>;
using ServiceWorkerOpPromise =
MozPromise<ServiceWorkerOpResult, nsresult, true>;

View File

@ -688,11 +688,11 @@ ServiceWorkerPrivateImpl::PendingFetchEvent::PendingFetchEvent(
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel>&& aChannel,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise)
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises)
: PendingFunctionalEvent(aOwner, std::move(aRegistration)),
mArgs(std::move(aArgs)),
mChannel(std::move(aChannel)),
mPreloadResponseReadyPromise(std::move(aPreloadResponseReadyPromise)) {
mPreloadResponseReadyPromises(std::move(aPreloadResponseReadyPromises)) {
AssertIsOnMainThread();
MOZ_ASSERT(mChannel);
}
@ -704,7 +704,7 @@ nsresult ServiceWorkerPrivateImpl::PendingFetchEvent::Send() {
return mOwner->SendFetchEventInternal(
std::move(mRegistration), std::move(mArgs), std::move(mChannel),
std::move(mPreloadResponseReadyPromise));
std::move(mPreloadResponseReadyPromises));
}
ServiceWorkerPrivateImpl::PendingFetchEvent::~PendingFetchEvent() {
@ -894,8 +894,7 @@ nsresult MaybeStoreStreamForBackgroundThread(nsIInterceptedChannel* aChannel,
} // anonymous namespace
RefPtr<FetchServiceResponsePromise>
ServiceWorkerPrivateImpl::SetupNavigationPreload(
RefPtr<FetchServicePromises> ServiceWorkerPrivateImpl::SetupNavigationPreload(
nsCOMPtr<nsIInterceptedChannel>& aChannel,
const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration) {
MOZ_ASSERT(XRE_IsParentProcess());
@ -980,9 +979,9 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
request.method().LowerCaseEqualsASCII("get") &&
aRegistration->GetNavigationPreloadState().enabled();
RefPtr<FetchServiceResponsePromise> preloadResponsePromise;
RefPtr<FetchServicePromises> preloadResponsePromises;
if (preloadNavigation) {
preloadResponsePromise = SetupNavigationPreload(aChannel, aRegistration);
preloadResponsePromises = SetupNavigationPreload(aChannel, aRegistration);
}
ParentToParentServiceWorkerFetchEventOpArgs args(
@ -990,13 +989,13 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
mOuter->mInfo->ScriptSpec(), request, nsString(aClientId),
nsString(aResultingClientId), isNonSubresourceRequest,
preloadNavigation, mOuter->mInfo->TestingInjectCancellation()),
Nothing());
Nothing(), Nothing());
if (mOuter->mInfo->State() == ServiceWorkerState::Activating) {
UniquePtr<PendingFunctionalEvent> pendingEvent =
MakeUnique<PendingFetchEvent>(this, std::move(aRegistration),
std::move(args), std::move(aChannel),
std::move(preloadResponsePromise));
std::move(preloadResponsePromises));
mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
@ -1007,14 +1006,14 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
return SendFetchEventInternal(std::move(aRegistration), std::move(args),
std::move(aChannel),
std::move(preloadResponsePromise));
std::move(preloadResponsePromises));
}
nsresult ServiceWorkerPrivateImpl::SendFetchEventInternal(
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel>&& aChannel,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise) {
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises) {
AssertIsOnMainThread();
MOZ_ASSERT(mOuter);
@ -1036,7 +1035,7 @@ nsresult ServiceWorkerPrivateImpl::SendFetchEventInternal(
FetchEventOpChild::SendFetchEvent(
mControllerChild->get(), std::move(aArgs), std::move(aChannel),
std::move(aRegistration), std::move(aPreloadResponseReadyPromise),
std::move(aRegistration), std::move(aPreloadResponseReadyPromises),
mOuter->CreateEventKeepAliveToken())
->Then(GetCurrentSerialEventTarget(), __func__,
[holder = std::move(holder)](

View File

@ -143,7 +143,7 @@ class ServiceWorkerPrivateImpl final : public ServiceWorkerPrivate::Inner,
// Setup the navigation preload by the intercepted channel and the
// RegistrationInfo.
RefPtr<FetchServiceResponsePromise> SetupNavigationPreload(
RefPtr<FetchServicePromises> SetupNavigationPreload(
nsCOMPtr<nsIInterceptedChannel>& aChannel,
const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration);
@ -151,7 +151,7 @@ class ServiceWorkerPrivateImpl final : public ServiceWorkerPrivate::Inner,
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel>&& aChannel,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise);
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises);
void Shutdown();
@ -197,7 +197,7 @@ class ServiceWorkerPrivateImpl final : public ServiceWorkerPrivate::Inner,
RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
nsCOMPtr<nsIInterceptedChannel>&& aChannel,
RefPtr<FetchServiceResponsePromise>&& aPreloadResponseReadyPromise);
RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises);
nsresult Send() override;
@ -206,13 +206,13 @@ class ServiceWorkerPrivateImpl final : public ServiceWorkerPrivate::Inner,
private:
ParentToParentServiceWorkerFetchEventOpArgs mArgs;
nsCOMPtr<nsIInterceptedChannel> mChannel;
// The promise from FetchService. It indicates if the preload response is
// The promises from FetchService. It indicates if the preload response is
// ready or not. The promise's resolve/reject value should be handled in
// FetchEventOpChild, such that the preload result can be propagated to the
// ServiceWorker through IPC. However, FetchEventOpChild creation could be
// pending here, so this member is needed. And it will be forwarded to
// FetchEventOpChild when crearting the FetchEventOpChild.
RefPtr<FetchServiceResponsePromise> mPreloadResponseReadyPromise;
RefPtr<FetchServicePromises> mPreloadResponseReadyPromises;
};
nsTArray<UniquePtr<PendingFunctionalEvent>> mPendingFunctionalEvents;