ClientSourceParent.cpp (8167B)
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 "ClientSourceParent.h" 8 9 #include "ClientHandleParent.h" 10 #include "ClientManagerService.h" 11 #include "ClientSourceOpParent.h" 12 #include "ClientValidation.h" 13 #include "mozilla/SchedulerGroup.h" 14 #include "mozilla/dom/ClientIPCTypes.h" 15 #include "mozilla/dom/ContentParent.h" 16 #include "mozilla/dom/PClientManagerParent.h" 17 #include "mozilla/dom/ServiceWorkerManager.h" 18 #include "mozilla/dom/ServiceWorkerUtils.h" 19 #include "mozilla/ipc/BackgroundParent.h" 20 21 namespace mozilla::dom { 22 23 using mozilla::ipc::AssertIsOnBackgroundThread; 24 using mozilla::ipc::BackgroundParent; 25 using mozilla::ipc::IPCResult; 26 using mozilla::ipc::PrincipalInfo; 27 28 mozilla::ipc::IPCResult ClientSourceParent::RecvWorkerSyncPing() { 29 AssertIsOnBackgroundThread(); 30 // Do nothing here. This is purely a sync message allowing the child to 31 // confirm that the actor has been created on the parent process. 32 return IPC_OK(); 33 } 34 35 IPCResult ClientSourceParent::RecvTeardown() { 36 (void)Send__delete__(this); 37 return IPC_OK(); 38 } 39 40 IPCResult ClientSourceParent::RecvExecutionReady( 41 const ClientSourceExecutionReadyArgs& aArgs) { 42 // Now that we have the creation URL for the Client we can do some validation 43 // to make sure the child actor is not giving us garbage. Since we validate 44 // on the child side as well we treat a failure here as fatal. 45 if (!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(), aArgs.url())) { 46 return IPC_FAIL(this, "Invalid creation URL!"); 47 } 48 49 mClientInfo.SetURL(aArgs.url()); 50 mClientInfo.SetFrameType(aArgs.frameType()); 51 mExecutionReady = true; 52 53 for (ClientHandleParent* handle : mHandleList) { 54 (void)handle->SendExecutionReady(mClientInfo.ToIPC()); 55 } 56 57 mExecutionReadyPromise.ResolveIfExists(true, __func__); 58 59 return IPC_OK(); 60 }; 61 62 IPCResult ClientSourceParent::RecvFreeze() { 63 #ifdef FUZZING_SNAPSHOT 64 if (mFrozen) { 65 return IPC_FAIL(this, "Freezing when already frozen"); 66 } 67 #endif 68 MOZ_DIAGNOSTIC_ASSERT(!mFrozen); 69 mFrozen = true; 70 71 return IPC_OK(); 72 } 73 74 IPCResult ClientSourceParent::RecvThaw() { 75 #ifdef FUZZING_SNAPSHOT 76 if (!mFrozen) { 77 return IPC_FAIL(this, "Thawing when not already frozen"); 78 } 79 #endif 80 MOZ_DIAGNOSTIC_ASSERT(mFrozen); 81 mFrozen = false; 82 return IPC_OK(); 83 } 84 85 IPCResult ClientSourceParent::RecvInheritController( 86 const ClientControlledArgs& aArgs) { 87 mController.reset(); 88 mController.emplace(aArgs.serviceWorker()); 89 90 // We must tell the parent-side SWM about this controller inheritance. 91 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 92 "ClientSourceParent::RecvInheritController", 93 [clientInfo = mClientInfo, controller = mController.ref()]() { 94 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 95 NS_ENSURE_TRUE_VOID(swm); 96 97 swm->NoteInheritedController(clientInfo, controller); 98 }); 99 100 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 101 102 return IPC_OK(); 103 } 104 105 IPCResult ClientSourceParent::RecvNoteDOMContentLoaded() { 106 if (mController.isSome()) { 107 nsCOMPtr<nsIRunnable> r = 108 NS_NewRunnableFunction("ClientSourceParent::RecvNoteDOMContentLoaded", 109 [clientInfo = mClientInfo]() { 110 RefPtr<ServiceWorkerManager> swm = 111 ServiceWorkerManager::GetInstance(); 112 NS_ENSURE_TRUE_VOID(swm); 113 114 swm->MaybeCheckNavigationUpdate(clientInfo); 115 }); 116 117 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 118 } 119 return IPC_OK(); 120 } 121 122 void ClientSourceParent::ActorDestroy(ActorDestroyReason aReason) { 123 DebugOnly<bool> removed = mService->RemoveSource(this); 124 MOZ_ASSERT(removed); 125 126 for (ClientHandleParent* handle : mHandleList.Clone()) { 127 // This should trigger DetachHandle() to be called removing 128 // the entry from the mHandleList. 129 (void)ClientHandleParent::Send__delete__(handle); 130 } 131 MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty()); 132 } 133 134 PClientSourceOpParent* ClientSourceParent::AllocPClientSourceOpParent( 135 const ClientOpConstructorArgs& aArgs) { 136 MOZ_ASSERT_UNREACHABLE( 137 "ClientSourceOpParent should be explicitly constructed."); 138 return nullptr; 139 } 140 141 bool ClientSourceParent::DeallocPClientSourceOpParent( 142 PClientSourceOpParent* aActor) { 143 delete aActor; 144 return true; 145 } 146 147 ClientSourceParent::ClientSourceParent( 148 const ClientSourceConstructorArgs& aArgs, 149 const Maybe<ContentParentId>& aContentParentId) 150 : mClientInfo(aArgs.id(), aArgs.agentClusterId(), aArgs.type(), 151 aArgs.principalInfo(), aArgs.creationTime(), aArgs.url(), 152 aArgs.frameType()), 153 mContentParentId(aContentParentId), 154 mService(ClientManagerService::GetOrCreateInstance()), 155 mExecutionReady(false), 156 mFrozen(false) {} 157 158 ClientSourceParent::~ClientSourceParent() { 159 MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty()); 160 161 mExecutionReadyPromise.RejectIfExists(NS_ERROR_FAILURE, __func__); 162 } 163 164 IPCResult ClientSourceParent::Init() { 165 // Ensure the principal is reasonable before adding ourself to the service. 166 // Since we validate the principal on the child side as well, any failure 167 // here is treated as fatal. 168 if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) { 169 mService->ForgetFutureSource(mClientInfo.ToIPC()); 170 return IPC_FAIL(Manager(), "Invalid PrincipalInfo!"); 171 } 172 173 // Its possible for AddSource() to fail if there is already an entry for 174 // our UUID. This should not normally happen, but could if someone is 175 // spoofing IPC messages. 176 if (NS_WARN_IF(!mService->AddSource(this))) { 177 return IPC_FAIL(Manager(), "Already registered!"); 178 } 179 180 return IPC_OK(); 181 } 182 183 const ClientInfo& ClientSourceParent::Info() const { return mClientInfo; } 184 185 bool ClientSourceParent::IsFrozen() const { return mFrozen; } 186 187 bool ClientSourceParent::ExecutionReady() const { return mExecutionReady; } 188 189 RefPtr<GenericNonExclusivePromise> ClientSourceParent::ExecutionReadyPromise() { 190 // Only call if ClientSourceParent::ExecutionReady() is false; otherwise, 191 // the promise will never resolve 192 MOZ_ASSERT(!mExecutionReady); 193 return mExecutionReadyPromise.Ensure(__func__); 194 } 195 196 const Maybe<ServiceWorkerDescriptor>& ClientSourceParent::GetController() 197 const { 198 return mController; 199 } 200 201 void ClientSourceParent::ClearController() { mController.reset(); } 202 203 void ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle) { 204 MOZ_DIAGNOSTIC_ASSERT(aClientHandle); 205 MOZ_ASSERT(!mHandleList.Contains(aClientHandle)); 206 mHandleList.AppendElement(aClientHandle); 207 } 208 209 void ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle) { 210 MOZ_DIAGNOSTIC_ASSERT(aClientHandle); 211 MOZ_ASSERT(mHandleList.Contains(aClientHandle)); 212 mHandleList.RemoveElement(aClientHandle); 213 } 214 215 RefPtr<ClientOpPromise> ClientSourceParent::StartOp( 216 ClientOpConstructorArgs&& aArgs) { 217 RefPtr<ClientOpPromise::Private> promise = 218 new ClientOpPromise::Private(__func__); 219 220 // If we are being controlled, remember that data before propagating 221 // on to the ClientSource. This must be set prior to triggering 222 // the controllerchange event from the ClientSource since some tests 223 // expect matchAll() to find the controlled client immediately after. 224 // If the control operation fails, then we reset the controller value 225 // to reflect the final state. 226 if (aArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) { 227 mController.reset(); 228 mController.emplace(aArgs.get_ClientControlledArgs().serviceWorker()); 229 } 230 231 // Constructor failure will reject the promise via ActorDestroy(). 232 ClientSourceOpParent* actor = 233 new ClientSourceOpParent(std::move(aArgs), promise); 234 (void)SendPClientSourceOpConstructor(actor, actor->Args()); 235 236 return promise; 237 } 238 239 } // namespace mozilla::dom