1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FetchEventOpProxyChild.h"
11 #include "mozilla/dom/FetchTypes.h"
12 #include "mozilla/dom/ServiceWorkerOpPromise.h"
15 #include "nsThreadUtils.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/Unused.h"
21 #include "mozilla/dom/InternalRequest.h"
22 #include "mozilla/dom/InternalResponse.h"
23 #include "mozilla/dom/RemoteWorkerChild.h"
24 #include "mozilla/dom/RemoteWorkerService.h"
25 #include "mozilla/dom/ServiceWorkerOp.h"
26 #include "mozilla/dom/WorkerCommon.h"
27 #include "mozilla/ipc/BackgroundChild.h"
28 #include "mozilla/ipc/IPCStreamUtils.h"
38 nsresult
GetIPCSynthesizeResponseArgs(
39 ChildToParentSynthesizeResponseArgs
* aIPCArgs
,
40 SynthesizeResponseArgs
&& aArgs
) {
41 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
43 auto [internalResponse
, closure
, timeStamps
] = std::move(aArgs
);
45 aIPCArgs
->closure() = std::move(closure
);
46 aIPCArgs
->timeStamps() = std::move(timeStamps
);
48 PBackgroundChild
* bgChild
= BackgroundChild::GetOrCreateForCurrentThread();
50 if (NS_WARN_IF(!bgChild
)) {
51 return NS_ERROR_DOM_INVALID_STATE_ERR
;
54 internalResponse
->ToChildToParentInternalResponse(
55 &aIPCArgs
->internalResponse(), bgChild
);
59 } // anonymous namespace
61 void FetchEventOpProxyChild::Initialize(
62 const ParentToChildServiceWorkerFetchEventOpArgs
& aArgs
) {
63 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
67 MakeSafeRefPtr
<InternalRequest
>(aArgs
.common().internalRequest());
69 if (aArgs
.common().preloadNavigation()) {
70 // We use synchronous task dispatch here to make sure that if the preload
71 // response arrived before we dispatch the fetch event, then the JS preload
72 // response promise will get resolved immediately.
73 mPreloadResponseAvailablePromise
=
74 MakeRefPtr
<FetchEventPreloadResponseAvailablePromise::Private
>(
76 mPreloadResponseAvailablePromise
->UseSynchronousTaskDispatch(__func__
);
77 if (aArgs
.preloadResponse().isSome()) {
78 mPreloadResponseAvailablePromise
->Resolve(
79 InternalResponse::FromIPC(aArgs
.preloadResponse().ref()), __func__
);
82 mPreloadResponseTimingPromise
=
83 MakeRefPtr
<FetchEventPreloadResponseTimingPromise::Private
>(__func__
);
84 mPreloadResponseTimingPromise
->UseSynchronousTaskDispatch(__func__
);
85 if (aArgs
.preloadResponseTiming().isSome()) {
86 mPreloadResponseTimingPromise
->Resolve(
87 aArgs
.preloadResponseTiming().ref(), __func__
);
90 mPreloadResponseEndPromise
=
91 MakeRefPtr
<FetchEventPreloadResponseEndPromise::Private
>(__func__
);
92 mPreloadResponseEndPromise
->UseSynchronousTaskDispatch(__func__
);
93 if (aArgs
.preloadResponseEndArgs().isSome()) {
94 mPreloadResponseEndPromise
->Resolve(aArgs
.preloadResponseEndArgs().ref(),
99 RemoteWorkerChild
* manager
= static_cast<RemoteWorkerChild
*>(Manager());
102 RefPtr
<FetchEventOpProxyChild
> self
= this;
104 auto callback
= [self
](const ServiceWorkerOpResult
& aResult
) {
105 // FetchEventOp could finish before NavigationPreload fetch finishes.
106 // If NavigationPreload is available in FetchEvent, caching FetchEventOp
107 // result until RecvPreloadResponseEnd is called, such that the preload
108 // response could be completed.
109 if (self
->mPreloadResponseEndPromise
&&
110 !self
->mPreloadResponseEndPromise
->IsResolved() &&
111 self
->mPreloadResponseAvailablePromise
->IsResolved()) {
112 self
->mCachedOpResult
= Some(aResult
);
115 if (!self
->CanSend()) {
119 if (NS_WARN_IF(aResult
.type() == ServiceWorkerOpResult::Tnsresult
)) {
120 Unused
<< self
->Send__delete__(self
, aResult
.get_nsresult());
124 MOZ_ASSERT(aResult
.type() ==
125 ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult
);
127 Unused
<< self
->Send__delete__(self
, aResult
);
130 RefPtr
<FetchEventOp
> op
= ServiceWorkerOp::Create(aArgs
, std::move(callback
))
131 .template downcast
<FetchEventOp
>();
138 op
->GetRespondWithPromise()
139 ->Then(GetCurrentSerialEventTarget(), __func__
,
140 [self
= std::move(self
)](
141 FetchEventRespondWithPromise::ResolveOrRejectValue
&& aResult
) {
142 self
->mRespondWithPromiseRequestHolder
.Complete();
144 if (NS_WARN_IF(aResult
.IsReject())) {
145 MOZ_ASSERT(NS_FAILED(aResult
.RejectValue().status()));
147 Unused
<< self
->SendRespondWith(aResult
.RejectValue());
151 auto& result
= aResult
.ResolveValue();
153 if (result
.is
<SynthesizeResponseArgs
>()) {
154 ChildToParentSynthesizeResponseArgs ipcArgs
;
155 nsresult rv
= GetIPCSynthesizeResponseArgs(
156 &ipcArgs
, result
.extract
<SynthesizeResponseArgs
>());
158 if (NS_WARN_IF(NS_FAILED(rv
))) {
159 Unused
<< self
->SendRespondWith(
160 CancelInterceptionArgs(rv
, ipcArgs
.timeStamps()));
164 Unused
<< self
->SendRespondWith(ipcArgs
);
165 } else if (result
.is
<ResetInterceptionArgs
>()) {
166 Unused
<< self
->SendRespondWith(
167 result
.extract
<ResetInterceptionArgs
>());
169 Unused
<< self
->SendRespondWith(
170 result
.extract
<CancelInterceptionArgs
>());
173 ->Track(mRespondWithPromiseRequestHolder
);
175 manager
->MaybeStartOp(std::move(op
));
178 SafeRefPtr
<InternalRequest
> FetchEventOpProxyChild::ExtractInternalRequest() {
179 MOZ_ASSERT(IsCurrentThreadRunningWorker());
180 MOZ_ASSERT(mInternalRequest
);
182 return std::move(mInternalRequest
);
185 RefPtr
<FetchEventPreloadResponseAvailablePromise
>
186 FetchEventOpProxyChild::GetPreloadResponseAvailablePromise() {
187 return mPreloadResponseAvailablePromise
;
190 RefPtr
<FetchEventPreloadResponseTimingPromise
>
191 FetchEventOpProxyChild::GetPreloadResponseTimingPromise() {
192 return mPreloadResponseTimingPromise
;
195 RefPtr
<FetchEventPreloadResponseEndPromise
>
196 FetchEventOpProxyChild::GetPreloadResponseEndPromise() {
197 return mPreloadResponseEndPromise
;
200 mozilla::ipc::IPCResult
FetchEventOpProxyChild::RecvPreloadResponse(
201 ParentToChildInternalResponse
&& aResponse
) {
202 // Receiving this message implies that navigation preload is enabled, so
203 // Initialize() should have created this promise.
204 MOZ_ASSERT(mPreloadResponseAvailablePromise
);
206 mPreloadResponseAvailablePromise
->Resolve(
207 InternalResponse::FromIPC(aResponse
), __func__
);
212 mozilla::ipc::IPCResult
FetchEventOpProxyChild::RecvPreloadResponseTiming(
213 ResponseTiming
&& aTiming
) {
214 // Receiving this message implies that navigation preload is enabled, so
215 // Initialize() should have created this promise.
216 MOZ_ASSERT(mPreloadResponseTimingPromise
);
218 mPreloadResponseTimingPromise
->Resolve(std::move(aTiming
), __func__
);
222 mozilla::ipc::IPCResult
FetchEventOpProxyChild::RecvPreloadResponseEnd(
223 ResponseEndArgs
&& aArgs
) {
224 // Receiving this message implies that navigation preload is enabled, so
225 // Initialize() should have created this promise.
226 MOZ_ASSERT(mPreloadResponseEndPromise
);
228 mPreloadResponseEndPromise
->Resolve(std::move(aArgs
), __func__
);
229 // If mCachedOpResult is not nothing, it means FetchEventOp had already done
230 // and the operation result is cached. Continue closing IPC here.
231 if (mCachedOpResult
.isNothing()) {
239 if (NS_WARN_IF(mCachedOpResult
.ref().type() ==
240 ServiceWorkerOpResult::Tnsresult
)) {
241 Unused
<< Send__delete__(this, mCachedOpResult
.ref().get_nsresult());
245 MOZ_ASSERT(mCachedOpResult
.ref().type() ==
246 ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult
);
248 Unused
<< Send__delete__(this, mCachedOpResult
.ref());
253 void FetchEventOpProxyChild::ActorDestroy(ActorDestroyReason
) {
254 Unused
<< NS_WARN_IF(mRespondWithPromiseRequestHolder
.Exists());
255 mRespondWithPromiseRequestHolder
.DisconnectIfExists();
257 // If mPreloadResponseAvailablePromise exists, navigation preloading response
258 // will not be valid anymore since it is too late to respond to the
259 // FetchEvent. Resolve the preload response promise with
260 // NS_ERROR_DOM_ABORT_ERR.
261 if (mPreloadResponseAvailablePromise
) {
262 mPreloadResponseAvailablePromise
->Resolve(
263 InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR
), __func__
);
266 if (mPreloadResponseTimingPromise
) {
267 mPreloadResponseTimingPromise
->Resolve(ResponseTiming(), __func__
);
270 if (mPreloadResponseEndPromise
) {
271 ResponseEndArgs
args(FetchDriverObserver::eAborted
);
272 mPreloadResponseEndPromise
->Resolve(args
, __func__
);
275 mOp
->RevokeActor(this);
280 } // namespace mozilla