Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / serviceworkers / FetchEventOpProxyChild.cpp
blob8e437356ec50dabcddf6119029a54afcc2efac21
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"
9 #include <utility>
11 #include "mozilla/dom/FetchTypes.h"
12 #include "mozilla/dom/ServiceWorkerOpPromise.h"
13 #include "nsCOMPtr.h"
14 #include "nsDebug.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"
30 namespace mozilla {
32 using namespace ipc;
34 namespace dom {
36 namespace {
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);
56 return NS_OK;
59 } // anonymous namespace
61 void FetchEventOpProxyChild::Initialize(
62 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
63 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
64 MOZ_ASSERT(!mOp);
66 mInternalRequest =
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>(
75 __func__);
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(),
95 __func__);
99 RemoteWorkerChild* manager = static_cast<RemoteWorkerChild*>(Manager());
100 MOZ_ASSERT(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);
113 return;
115 if (!self->CanSend()) {
116 return;
119 if (NS_WARN_IF(aResult.type() == ServiceWorkerOpResult::Tnsresult)) {
120 Unused << self->Send__delete__(self, aResult.get_nsresult());
121 return;
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>();
133 MOZ_ASSERT(op);
135 op->SetActor(this);
136 mOp = op;
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());
148 return;
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()));
161 return;
164 Unused << self->SendRespondWith(ipcArgs);
165 } else if (result.is<ResetInterceptionArgs>()) {
166 Unused << self->SendRespondWith(
167 result.extract<ResetInterceptionArgs>());
168 } else {
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__);
209 return IPC_OK();
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__);
219 return IPC_OK();
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()) {
232 return IPC_OK();
235 if (!CanSend()) {
236 return IPC_OK();
239 if (NS_WARN_IF(mCachedOpResult.ref().type() ==
240 ServiceWorkerOpResult::Tnsresult)) {
241 Unused << Send__delete__(this, mCachedOpResult.ref().get_nsresult());
242 return IPC_OK();
245 MOZ_ASSERT(mCachedOpResult.ref().type() ==
246 ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult);
248 Unused << Send__delete__(this, mCachedOpResult.ref());
250 return IPC_OK();
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);
276 mOp = nullptr;
279 } // namespace dom
280 } // namespace mozilla