/src/mozilla-central/dom/clients/manager/ClientSourceParent.cpp
Line | Count | Source (jump to first uncovered line) |
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/dom/ClientIPCTypes.h" |
14 | | #include "mozilla/dom/ContentParent.h" |
15 | | #include "mozilla/dom/PClientManagerParent.h" |
16 | | #include "mozilla/ipc/BackgroundParent.h" |
17 | | #include "mozilla/SystemGroup.h" |
18 | | #include "mozilla/Unused.h" |
19 | | |
20 | | namespace mozilla { |
21 | | namespace dom { |
22 | | |
23 | | using mozilla::ipc::AssertIsOnBackgroundThread; |
24 | | using mozilla::ipc::BackgroundParent; |
25 | | using mozilla::ipc::IPCResult; |
26 | | using mozilla::ipc::PrincipalInfo; |
27 | | |
28 | | namespace { |
29 | | |
30 | | // It would be nice to use a lambda instead of this class, but we cannot |
31 | | // move capture in lambdas yet and ContentParent cannot be AddRef'd off |
32 | | // the main thread. |
33 | | class KillContentParentRunnable final : public Runnable |
34 | | { |
35 | | RefPtr<ContentParent> mContentParent; |
36 | | |
37 | | public: |
38 | | explicit KillContentParentRunnable(RefPtr<ContentParent>&& aContentParent) |
39 | | : Runnable("KillContentParentRunnable") |
40 | | , mContentParent(std::move(aContentParent)) |
41 | 0 | { |
42 | 0 | MOZ_ASSERT(mContentParent); |
43 | 0 | } |
44 | | |
45 | | NS_IMETHOD |
46 | | Run() override |
47 | 0 | { |
48 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
49 | 0 | mContentParent->KillHard("invalid ClientSourceParent actor"); |
50 | 0 | mContentParent = nullptr; |
51 | 0 | return NS_OK; |
52 | 0 | } |
53 | | }; |
54 | | |
55 | | } // anonymous namespace |
56 | | |
57 | | void |
58 | | ClientSourceParent::KillInvalidChild() |
59 | 0 | { |
60 | 0 | // Try to get the content process before we destroy the actor below. |
61 | 0 | RefPtr<ContentParent> process = |
62 | 0 | BackgroundParent::GetContentParent(Manager()->Manager()); |
63 | 0 |
|
64 | 0 | // First, immediately teardown the ClientSource actor. No matter what |
65 | 0 | // we want to start this process as soon as possible. |
66 | 0 | Unused << ClientSourceParent::Send__delete__(this); |
67 | 0 |
|
68 | 0 | // If we are running in non-e10s, then there is nothing else to do here. |
69 | 0 | // There is no child process and we don't want to crash the entire browser |
70 | 0 | // in release builds. In general, though, this should not happen in non-e10s |
71 | 0 | // so we do assert this condition. |
72 | 0 | if (!process) { |
73 | 0 | MOZ_DIAGNOSTIC_ASSERT(false, "invalid ClientSourceParent in non-e10s"); |
74 | 0 | return; |
75 | 0 | } |
76 | 0 | |
77 | 0 | // In e10s mode we also want to kill the child process. Validation failures |
78 | 0 | // typically mean someone sent us bogus data over the IPC link. We can't |
79 | 0 | // trust that process any more. We have to do this on the main thread, so |
80 | 0 | // there is a small window of time before we kill the process. This is why |
81 | 0 | // we start the actor destruction immediately above. |
82 | 0 | nsCOMPtr<nsIRunnable> r = new KillContentParentRunnable(std::move(process)); |
83 | 0 | MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); |
84 | 0 | } |
85 | | |
86 | | mozilla::ipc::IPCResult |
87 | | ClientSourceParent::RecvWorkerSyncPing() |
88 | 0 | { |
89 | 0 | AssertIsOnBackgroundThread(); |
90 | 0 | // Do nothing here. This is purely a sync message allowing the child to |
91 | 0 | // confirm that the actor has been created on the parent process. |
92 | 0 | return IPC_OK(); |
93 | 0 | } |
94 | | |
95 | | IPCResult |
96 | | ClientSourceParent::RecvTeardown() |
97 | 0 | { |
98 | 0 | Unused << Send__delete__(this); |
99 | 0 | return IPC_OK(); |
100 | 0 | } |
101 | | |
102 | | IPCResult |
103 | | ClientSourceParent::RecvExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) |
104 | 0 | { |
105 | 0 | // Now that we have the creation URL for the Client we can do some validation |
106 | 0 | // to make sure the child actor is not giving us garbage. Since we validate |
107 | 0 | // on the child side as well we treat a failure here as fatal. |
108 | 0 | if (!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(), aArgs.url())) { |
109 | 0 | KillInvalidChild(); |
110 | 0 | return IPC_OK(); |
111 | 0 | } |
112 | 0 |
|
113 | 0 | mClientInfo.SetURL(aArgs.url()); |
114 | 0 | mClientInfo.SetFrameType(aArgs.frameType()); |
115 | 0 | mExecutionReady = true; |
116 | 0 |
|
117 | 0 | for (ClientHandleParent* handle : mHandleList) { |
118 | 0 | Unused << handle->SendExecutionReady(mClientInfo.ToIPC()); |
119 | 0 | } |
120 | 0 |
|
121 | 0 | return IPC_OK(); |
122 | 0 | }; |
123 | | |
124 | | IPCResult |
125 | | ClientSourceParent::RecvFreeze() |
126 | 0 | { |
127 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mFrozen); |
128 | 0 | mFrozen = true; |
129 | 0 |
|
130 | 0 | // Frozen clients should not be observable. Act as if the client has |
131 | 0 | // been destroyed. |
132 | 0 | nsTArray<ClientHandleParent*> handleList(mHandleList); |
133 | 0 | for (ClientHandleParent* handle : handleList) { |
134 | 0 | Unused << ClientHandleParent::Send__delete__(handle); |
135 | 0 | } |
136 | 0 |
|
137 | 0 | return IPC_OK(); |
138 | 0 | } |
139 | | |
140 | | IPCResult |
141 | | ClientSourceParent::RecvThaw() |
142 | 0 | { |
143 | 0 | MOZ_DIAGNOSTIC_ASSERT(mFrozen); |
144 | 0 | mFrozen = false; |
145 | 0 | return IPC_OK(); |
146 | 0 | } |
147 | | |
148 | | IPCResult |
149 | | ClientSourceParent::RecvInheritController(const ClientControlledArgs& aArgs) |
150 | 0 | { |
151 | 0 | mController.reset(); |
152 | 0 | mController.emplace(aArgs.serviceWorker()); |
153 | 0 |
|
154 | 0 | // In parent-side intercept mode we must tell the parent-side SWM about |
155 | 0 | // this controller inheritence. In legacy client-side mode this is done |
156 | 0 | // from the ClientSource instead. |
157 | 0 | if (!ServiceWorkerParentInterceptEnabled()) { |
158 | 0 | nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( |
159 | 0 | "ClientSourceParent::RecvInheritController", |
160 | 0 | [clientInfo = mClientInfo, controller = mController.ref()] () { |
161 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
162 | 0 | NS_ENSURE_TRUE_VOID(swm); |
163 | 0 |
|
164 | 0 | swm->NoteInheritedController(clientInfo, controller); |
165 | 0 | }); |
166 | 0 |
|
167 | 0 | MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); |
168 | 0 | } |
169 | 0 |
|
170 | 0 | return IPC_OK(); |
171 | 0 | } |
172 | | |
173 | | IPCResult |
174 | | ClientSourceParent::RecvNoteDOMContentLoaded() |
175 | 0 | { |
176 | 0 | if (mController.isSome() && ServiceWorkerParentInterceptEnabled()) { |
177 | 0 | nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( |
178 | 0 | "ClientSourceParent::RecvNoteDOMContentLoaded", |
179 | 0 | [clientInfo = mClientInfo] () { |
180 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
181 | 0 | NS_ENSURE_TRUE_VOID(swm); |
182 | 0 |
|
183 | 0 | swm->MaybeCheckNavigationUpdate(clientInfo); |
184 | 0 | }); |
185 | 0 |
|
186 | 0 | MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget())); |
187 | 0 | } |
188 | 0 | return IPC_OK(); |
189 | 0 | } |
190 | | |
191 | | void |
192 | | ClientSourceParent::ActorDestroy(ActorDestroyReason aReason) |
193 | 0 | { |
194 | 0 | DebugOnly<bool> removed = mService->RemoveSource(this); |
195 | 0 | MOZ_ASSERT(removed); |
196 | 0 |
|
197 | 0 | nsTArray<ClientHandleParent*> handleList(mHandleList); |
198 | 0 | for (ClientHandleParent* handle : handleList) { |
199 | 0 | // This should trigger DetachHandle() to be called removing |
200 | 0 | // the entry from the mHandleList. |
201 | 0 | Unused << ClientHandleParent::Send__delete__(handle); |
202 | 0 | } |
203 | 0 | MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty()); |
204 | 0 | } |
205 | | |
206 | | PClientSourceOpParent* |
207 | | ClientSourceParent::AllocPClientSourceOpParent(const ClientOpConstructorArgs& aArgs) |
208 | 0 | { |
209 | 0 | MOZ_ASSERT_UNREACHABLE("ClientSourceOpParent should be explicitly constructed."); |
210 | 0 | return nullptr; |
211 | 0 | } |
212 | | |
213 | | bool |
214 | | ClientSourceParent::DeallocPClientSourceOpParent(PClientSourceOpParent* aActor) |
215 | 0 | { |
216 | 0 | delete aActor; |
217 | 0 | return true; |
218 | 0 | } |
219 | | |
220 | | ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs) |
221 | | : mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime()) |
222 | | , mService(ClientManagerService::GetOrCreateInstance()) |
223 | | , mExecutionReady(false) |
224 | | , mFrozen(false) |
225 | 0 | { |
226 | 0 | } |
227 | | |
228 | | ClientSourceParent::~ClientSourceParent() |
229 | 0 | { |
230 | 0 | MOZ_DIAGNOSTIC_ASSERT(mHandleList.IsEmpty()); |
231 | 0 | } |
232 | | |
233 | | void |
234 | | ClientSourceParent::Init() |
235 | 0 | { |
236 | 0 | // Ensure the principal is reasonable before adding ourself to the service. |
237 | 0 | // Since we validate the principal on the child side as well, any failure |
238 | 0 | // here is treated as fatal. |
239 | 0 | if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) { |
240 | 0 | KillInvalidChild(); |
241 | 0 | return; |
242 | 0 | } |
243 | 0 | |
244 | 0 | // Its possible for AddSource() to fail if there is already an entry for |
245 | 0 | // our UUID. This should not normally happen, but could if someone is |
246 | 0 | // spoofing IPC messages. |
247 | 0 | if (NS_WARN_IF(!mService->AddSource(this))) { |
248 | 0 | KillInvalidChild(); |
249 | 0 | return; |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | const ClientInfo& |
254 | | ClientSourceParent::Info() const |
255 | 0 | { |
256 | 0 | return mClientInfo; |
257 | 0 | } |
258 | | |
259 | | bool |
260 | | ClientSourceParent::IsFrozen() const |
261 | 0 | { |
262 | 0 | return mFrozen; |
263 | 0 | } |
264 | | |
265 | | bool |
266 | | ClientSourceParent::ExecutionReady() const |
267 | 0 | { |
268 | 0 | return mExecutionReady; |
269 | 0 | } |
270 | | |
271 | | const Maybe<ServiceWorkerDescriptor>& |
272 | | ClientSourceParent::GetController() const |
273 | 0 | { |
274 | 0 | return mController; |
275 | 0 | } |
276 | | |
277 | | void |
278 | | ClientSourceParent::ClearController() |
279 | 0 | { |
280 | 0 | mController.reset(); |
281 | 0 | } |
282 | | |
283 | | void |
284 | | ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle) |
285 | 0 | { |
286 | 0 | MOZ_DIAGNOSTIC_ASSERT(aClientHandle); |
287 | 0 | MOZ_DIAGNOSTIC_ASSERT(!mFrozen); |
288 | 0 | MOZ_ASSERT(!mHandleList.Contains(aClientHandle)); |
289 | 0 | mHandleList.AppendElement(aClientHandle); |
290 | 0 | } |
291 | | |
292 | | void |
293 | | ClientSourceParent::DetachHandle(ClientHandleParent* aClientHandle) |
294 | 0 | { |
295 | 0 | MOZ_DIAGNOSTIC_ASSERT(aClientHandle); |
296 | 0 | MOZ_ASSERT(mHandleList.Contains(aClientHandle)); |
297 | 0 | mHandleList.RemoveElement(aClientHandle); |
298 | 0 | } |
299 | | |
300 | | RefPtr<ClientOpPromise> |
301 | | ClientSourceParent::StartOp(const ClientOpConstructorArgs& aArgs) |
302 | 0 | { |
303 | 0 | RefPtr<ClientOpPromise::Private> promise = |
304 | 0 | new ClientOpPromise::Private(__func__); |
305 | 0 |
|
306 | 0 | // If we are being controlled, remember that data before propagating |
307 | 0 | // on to the ClientSource. This must be set prior to triggering |
308 | 0 | // the controllerchange event from the ClientSource since some tests |
309 | 0 | // expect matchAll() to find the controlled client immediately after. |
310 | 0 | // If the control operation fails, then we reset the controller value |
311 | 0 | // to reflect the final state. |
312 | 0 | if (aArgs.type() == ClientOpConstructorArgs::TClientControlledArgs) { |
313 | 0 | mController.reset(); |
314 | 0 | mController.emplace(aArgs.get_ClientControlledArgs().serviceWorker()); |
315 | 0 | } |
316 | 0 |
|
317 | 0 | // Constructor failure will reject the promise via ActorDestroy(). |
318 | 0 | ClientSourceOpParent* actor = new ClientSourceOpParent(aArgs, promise); |
319 | 0 | Unused << SendPClientSourceOpConstructor(actor, aArgs); |
320 | 0 |
|
321 | 0 | return promise.forget(); |
322 | 0 | } |
323 | | |
324 | | } // namespace dom |
325 | | } // namespace mozilla |