FetchEventOpProxyChild.cpp (9777B)
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/. */ 6 7 #include "FetchEventOpProxyChild.h" 8 9 #include <utility> 10 11 #include "mozilla/Assertions.h" 12 #include "mozilla/RefPtr.h" 13 #include "mozilla/dom/FetchTypes.h" 14 #include "mozilla/dom/InternalRequest.h" 15 #include "mozilla/dom/InternalResponse.h" 16 #include "mozilla/dom/RemoteWorkerChild.h" 17 #include "mozilla/dom/RemoteWorkerService.h" 18 #include "mozilla/dom/ServiceWorkerOp.h" 19 #include "mozilla/dom/ServiceWorkerOpPromise.h" 20 #include "mozilla/dom/WorkerCommon.h" 21 #include "mozilla/ipc/BackgroundChild.h" 22 #include "mozilla/ipc/IPCStreamUtils.h" 23 #include "nsCOMPtr.h" 24 #include "nsDebug.h" 25 #include "nsThreadUtils.h" 26 27 namespace mozilla { 28 29 using namespace ipc; 30 31 namespace dom { 32 33 namespace { 34 35 nsresult GetIPCSynthesizeResponseArgs( 36 ChildToParentSynthesizeResponseArgs* aIPCArgs, 37 SynthesizeResponseArgs&& aArgs) { 38 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread()); 39 40 auto [internalResponse, closure, timeStamps] = std::move(aArgs); 41 42 aIPCArgs->closure() = std::move(closure); 43 aIPCArgs->timeStamps() = std::move(timeStamps); 44 45 PBackgroundChild* bgChild = BackgroundChild::GetOrCreateForCurrentThread(); 46 47 if (NS_WARN_IF(!bgChild)) { 48 return NS_ERROR_DOM_INVALID_STATE_ERR; 49 } 50 51 internalResponse->ToChildToParentInternalResponse( 52 &aIPCArgs->internalResponse(), bgChild); 53 return NS_OK; 54 } 55 56 } // anonymous namespace 57 58 void FetchEventOpProxyChild::Initialize( 59 const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) { 60 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread()); 61 MOZ_ASSERT(!mOp); 62 63 mInternalRequest = 64 MakeSafeRefPtr<InternalRequest>(aArgs.common().internalRequest()); 65 66 if (aArgs.common().preloadNavigation()) { 67 // We use synchronous task dispatch here to make sure that if the preload 68 // response arrived before we dispatch the fetch event, then the JS preload 69 // response promise will get resolved immediately. 70 mPreloadResponseAvailablePromise = 71 MakeRefPtr<FetchEventPreloadResponseAvailablePromise::Private>( 72 __func__); 73 mPreloadResponseAvailablePromise->UseDirectTaskDispatch(__func__); 74 if (aArgs.preloadResponse().isSome()) { 75 mPreloadResponseAvailablePromiseResolved = true; 76 mPreloadResponseAvailablePromise->Resolve( 77 InternalResponse::FromIPC(aArgs.preloadResponse().ref()), __func__); 78 } 79 80 mPreloadResponseTimingPromise = 81 MakeRefPtr<FetchEventPreloadResponseTimingPromise::Private>(__func__); 82 mPreloadResponseTimingPromise->UseDirectTaskDispatch(__func__); 83 if (aArgs.preloadResponseTiming().isSome()) { 84 mPreloadResponseTimingPromise->Resolve( 85 aArgs.preloadResponseTiming().ref(), __func__); 86 } 87 88 mPreloadResponseEndPromise = 89 MakeRefPtr<FetchEventPreloadResponseEndPromise::Private>(__func__); 90 mPreloadResponseEndPromise->UseDirectTaskDispatch(__func__); 91 if (aArgs.preloadResponseEndArgs().isSome()) { 92 mPreloadResponseEndPromiseResolved = true; 93 mPreloadResponseEndPromise->Resolve(aArgs.preloadResponseEndArgs().ref(), 94 __func__); 95 } 96 } 97 98 RemoteWorkerChild* manager = static_cast<RemoteWorkerChild*>(Manager()); 99 MOZ_ASSERT(manager); 100 101 RefPtr<FetchEventOpProxyChild> self = this; 102 103 auto callback = [self](const ServiceWorkerOpResult& aResult) { 104 // FetchEventOp could finish before NavigationPreload fetch finishes. 105 // If NavigationPreload is available in FetchEvent, caching FetchEventOp 106 // result until RecvPreloadResponseEnd is called, such that the preload 107 // response could be completed. 108 if (self->mPreloadResponseEndPromise && 109 !self->mPreloadResponseEndPromiseResolved && 110 self->mPreloadResponseAvailablePromiseResolved) { 111 self->mCachedOpResult = Some(aResult); 112 return; 113 } 114 if (!self->CanSend()) { 115 return; 116 } 117 118 if (NS_WARN_IF(aResult.type() == ServiceWorkerOpResult::Tnsresult)) { 119 (void)self->Send__delete__(self, aResult.get_nsresult()); 120 return; 121 } 122 123 MOZ_ASSERT(aResult.type() == 124 ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult); 125 126 (void)self->Send__delete__(self, aResult); 127 }; 128 129 RefPtr<FetchEventOp> op = ServiceWorkerOp::Create(aArgs, std::move(callback)) 130 .template downcast<FetchEventOp>(); 131 132 MOZ_ASSERT(op); 133 134 op->SetActor(this); 135 mOp = op; 136 137 op->GetRespondWithPromise() 138 ->Then(GetCurrentSerialEventTarget(), __func__, 139 [self = std::move(self)]( 140 FetchEventRespondWithPromise::ResolveOrRejectValue&& aResult) { 141 self->mRespondWithPromiseRequestHolder.Complete(); 142 143 if (NS_WARN_IF(aResult.IsReject())) { 144 MOZ_ASSERT(NS_FAILED(aResult.RejectValue().status())); 145 146 (void)self->SendRespondWith(aResult.RejectValue()); 147 return; 148 } 149 150 auto& result = aResult.ResolveValue(); 151 152 if (result.is<SynthesizeResponseArgs>()) { 153 ChildToParentSynthesizeResponseArgs ipcArgs; 154 nsresult rv = GetIPCSynthesizeResponseArgs( 155 &ipcArgs, result.extract<SynthesizeResponseArgs>()); 156 157 if (NS_WARN_IF(NS_FAILED(rv))) { 158 (void)self->SendRespondWith( 159 CancelInterceptionArgs(rv, ipcArgs.timeStamps())); 160 return; 161 } 162 163 (void)self->SendRespondWith(ipcArgs); 164 } else if (result.is<ResetInterceptionArgs>()) { 165 (void)self->SendRespondWith( 166 result.extract<ResetInterceptionArgs>()); 167 } else { 168 (void)self->SendRespondWith( 169 result.extract<CancelInterceptionArgs>()); 170 } 171 }) 172 ->Track(mRespondWithPromiseRequestHolder); 173 174 manager->MaybeStartOp(std::move(op)); 175 } 176 177 SafeRefPtr<InternalRequest> FetchEventOpProxyChild::ExtractInternalRequest() { 178 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 179 MOZ_ASSERT(mInternalRequest); 180 181 return std::move(mInternalRequest); 182 } 183 184 RefPtr<FetchEventPreloadResponseAvailablePromise> 185 FetchEventOpProxyChild::GetPreloadResponseAvailablePromise() { 186 return mPreloadResponseAvailablePromise; 187 } 188 189 RefPtr<FetchEventPreloadResponseTimingPromise> 190 FetchEventOpProxyChild::GetPreloadResponseTimingPromise() { 191 return mPreloadResponseTimingPromise; 192 } 193 194 RefPtr<FetchEventPreloadResponseEndPromise> 195 FetchEventOpProxyChild::GetPreloadResponseEndPromise() { 196 return mPreloadResponseEndPromise; 197 } 198 199 mozilla::ipc::IPCResult FetchEventOpProxyChild::RecvPreloadResponse( 200 ParentToChildInternalResponse&& aResponse) { 201 // Receiving this message implies that navigation preload is enabled, so 202 // Initialize() should have created this promise. 203 MOZ_ASSERT(mPreloadResponseAvailablePromise); 204 205 mPreloadResponseAvailablePromiseResolved = true; 206 mPreloadResponseAvailablePromise->Resolve( 207 InternalResponse::FromIPC(aResponse), __func__); 208 209 return IPC_OK(); 210 } 211 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); 217 218 mPreloadResponseTimingPromise->Resolve(std::move(aTiming), __func__); 219 return IPC_OK(); 220 } 221 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); 227 228 mPreloadResponseEndPromiseResolved = true; 229 mPreloadResponseEndPromise->Resolve(std::move(aArgs), __func__); 230 // If mCachedOpResult is not nothing, it means FetchEventOp had already done 231 // and the operation result is cached. Continue closing IPC here. 232 if (mCachedOpResult.isNothing()) { 233 return IPC_OK(); 234 } 235 236 if (!CanSend()) { 237 return IPC_OK(); 238 } 239 240 if (NS_WARN_IF(mCachedOpResult.ref().type() == 241 ServiceWorkerOpResult::Tnsresult)) { 242 (void)Send__delete__(this, mCachedOpResult.ref().get_nsresult()); 243 return IPC_OK(); 244 } 245 246 MOZ_ASSERT(mCachedOpResult.ref().type() == 247 ServiceWorkerOpResult::TServiceWorkerFetchEventOpResult); 248 249 (void)Send__delete__(this, mCachedOpResult.ref()); 250 251 return IPC_OK(); 252 } 253 254 void FetchEventOpProxyChild::ActorDestroy(ActorDestroyReason) { 255 (void)NS_WARN_IF(mRespondWithPromiseRequestHolder.Exists()); 256 mRespondWithPromiseRequestHolder.DisconnectIfExists(); 257 258 // If mPreloadResponseAvailablePromise exists, navigation preloading response 259 // will not be valid anymore since it is too late to respond to the 260 // FetchEvent. Resolve the preload response promise with 261 // NS_ERROR_DOM_ABORT_ERR. 262 if (mPreloadResponseAvailablePromise) { 263 mPreloadResponseAvailablePromiseResolved = true; 264 mPreloadResponseAvailablePromise->Resolve( 265 InternalResponse::NetworkError(NS_ERROR_DOM_ABORT_ERR), __func__); 266 } 267 268 if (mPreloadResponseTimingPromise) { 269 mPreloadResponseTimingPromise->Resolve(ResponseTiming(), __func__); 270 } 271 272 if (mPreloadResponseEndPromise) { 273 mPreloadResponseEndPromiseResolved = true; 274 ResponseEndArgs args(FetchDriverObserver::eAborted); 275 mPreloadResponseEndPromise->Resolve(args, __func__); 276 } 277 278 mOp->RevokeActor(this); 279 mOp = nullptr; 280 } 281 282 } // namespace dom 283 } // namespace mozilla