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 "ClientSourceParent.h"
9 #include "ClientHandleParent.h"
10 #include "ClientManagerService.h"
11 #include "ClientSourceOpParent.h"
12 #include "ClientValidation.h"
13 #include "mozilla/dom/ClientIPCTypes.h"
14 #include "mozilla/dom/ContentParent.h"
15 #include "mozilla/dom/PClientManagerParent.h"
16 #include "mozilla/dom/ServiceWorkerManager.h"
17 #include "mozilla/dom/ServiceWorkerUtils.h"
18 #include "mozilla/ipc/BackgroundParent.h"
19 #include "mozilla/SchedulerGroup.h"
20 #include "mozilla/Unused.h"
22 namespace mozilla::dom
{
24 using mozilla::ipc::AssertIsOnBackgroundThread
;
25 using mozilla::ipc::BackgroundParent
;
26 using mozilla::ipc::IPCResult
;
27 using mozilla::ipc::PrincipalInfo
;
29 mozilla::ipc::IPCResult
ClientSourceParent::RecvWorkerSyncPing() {
30 AssertIsOnBackgroundThread();
31 // Do nothing here. This is purely a sync message allowing the child to
32 // confirm that the actor has been created on the parent process.
36 IPCResult
ClientSourceParent::RecvTeardown() {
37 Unused
<< Send__delete__(this);
41 IPCResult
ClientSourceParent::RecvExecutionReady(
42 const ClientSourceExecutionReadyArgs
& aArgs
) {
43 // Now that we have the creation URL for the Client we can do some validation
44 // to make sure the child actor is not giving us garbage. Since we validate
45 // on the child side as well we treat a failure here as fatal.
46 if (!ClientIsValidCreationURL(mClientInfo
.PrincipalInfo(), aArgs
.url())) {
47 return IPC_FAIL(this, "Invalid creation URL!");
50 mClientInfo
.SetURL(aArgs
.url());
51 mClientInfo
.SetFrameType(aArgs
.frameType());
52 mExecutionReady
= true;
54 for (ClientHandleParent
* handle
: mHandleList
) {
55 Unused
<< handle
->SendExecutionReady(mClientInfo
.ToIPC());
58 mExecutionReadyPromise
.ResolveIfExists(true, __func__
);
63 IPCResult
ClientSourceParent::RecvFreeze() {
64 #ifdef FUZZING_SNAPSHOT
66 return IPC_FAIL(this, "Freezing when already frozen");
69 MOZ_DIAGNOSTIC_ASSERT(!mFrozen
);
75 IPCResult
ClientSourceParent::RecvThaw() {
76 #ifdef FUZZING_SNAPSHOT
78 return IPC_FAIL(this, "Thawing when not already frozen");
81 MOZ_DIAGNOSTIC_ASSERT(mFrozen
);
86 IPCResult
ClientSourceParent::RecvInheritController(
87 const ClientControlledArgs
& aArgs
) {
89 mController
.emplace(aArgs
.serviceWorker());
91 // We must tell the parent-side SWM about this controller inheritance.
92 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
93 "ClientSourceParent::RecvInheritController",
94 [clientInfo
= mClientInfo
, controller
= mController
.ref()]() {
95 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
96 NS_ENSURE_TRUE_VOID(swm
);
98 swm
->NoteInheritedController(clientInfo
, controller
);
101 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
106 IPCResult
ClientSourceParent::RecvNoteDOMContentLoaded() {
107 if (mController
.isSome()) {
108 nsCOMPtr
<nsIRunnable
> r
=
109 NS_NewRunnableFunction("ClientSourceParent::RecvNoteDOMContentLoaded",
110 [clientInfo
= mClientInfo
]() {
111 RefPtr
<ServiceWorkerManager
> swm
=
112 ServiceWorkerManager::GetInstance();
113 NS_ENSURE_TRUE_VOID(swm
);
115 swm
->MaybeCheckNavigationUpdate(clientInfo
);
118 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
123 void ClientSourceParent::ActorDestroy(ActorDestroyReason aReason
) {
124 DebugOnly
<bool> removed
= mService
->RemoveSource(this);
127 for (ClientHandleParent
* handle
: mHandleList
.Clone()) {
128 // This should trigger DetachHandle() to be called removing
129 // the entry from the mHandleList.
130 Unused
<< ClientHandleParent::Send__delete__(handle
);
132 MOZ_DIAGNOSTIC_ASSERT(mHandleList
.IsEmpty());
135 PClientSourceOpParent
* ClientSourceParent::AllocPClientSourceOpParent(
136 const ClientOpConstructorArgs
& aArgs
) {
137 MOZ_ASSERT_UNREACHABLE(
138 "ClientSourceOpParent should be explicitly constructed.");
142 bool ClientSourceParent::DeallocPClientSourceOpParent(
143 PClientSourceOpParent
* aActor
) {
148 ClientSourceParent::ClientSourceParent(
149 const ClientSourceConstructorArgs
& aArgs
,
150 const Maybe
<ContentParentId
>& aContentParentId
)
151 : mClientInfo(aArgs
.id(), aArgs
.type(), aArgs
.principalInfo(),
152 aArgs
.creationTime()),
153 mContentParentId(aContentParentId
),
154 mService(ClientManagerService::GetOrCreateInstance()),
155 mExecutionReady(false),
158 ClientSourceParent::~ClientSourceParent() {
159 MOZ_DIAGNOSTIC_ASSERT(mHandleList
.IsEmpty());
161 mExecutionReadyPromise
.RejectIfExists(NS_ERROR_FAILURE
, __func__
);
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!");
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!");
183 const ClientInfo
& ClientSourceParent::Info() const { return mClientInfo
; }
185 bool ClientSourceParent::IsFrozen() const { return mFrozen
; }
187 bool ClientSourceParent::ExecutionReady() const { return mExecutionReady
; }
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__
);
196 const Maybe
<ServiceWorkerDescriptor
>& ClientSourceParent::GetController()
201 void ClientSourceParent::ClearController() { mController
.reset(); }
203 void ClientSourceParent::AttachHandle(ClientHandleParent
* aClientHandle
) {
204 MOZ_DIAGNOSTIC_ASSERT(aClientHandle
);
205 MOZ_ASSERT(!mHandleList
.Contains(aClientHandle
));
206 mHandleList
.AppendElement(aClientHandle
);
209 void ClientSourceParent::DetachHandle(ClientHandleParent
* aClientHandle
) {
210 MOZ_DIAGNOSTIC_ASSERT(aClientHandle
);
211 MOZ_ASSERT(mHandleList
.Contains(aClientHandle
));
212 mHandleList
.RemoveElement(aClientHandle
);
215 RefPtr
<ClientOpPromise
> ClientSourceParent::StartOp(
216 ClientOpConstructorArgs
&& aArgs
) {
217 RefPtr
<ClientOpPromise::Private
> promise
=
218 new ClientOpPromise::Private(__func__
);
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
) {
228 mController
.emplace(aArgs
.get_ClientControlledArgs().serviceWorker());
231 // Constructor failure will reject the promise via ActorDestroy().
232 ClientSourceOpParent
* actor
=
233 new ClientSourceOpParent(std::move(aArgs
), promise
);
234 Unused
<< SendPClientSourceOpConstructor(actor
, actor
->Args());
239 } // namespace mozilla::dom