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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "DCPresentationChannelDescription.h"
8 #include "mozilla/dom/ContentProcessManager.h"
9 #include "mozilla/dom/Element.h"
10 #include "mozilla/ipc/InputStreamUtils.h"
11 #include "mozilla/Unused.h"
12 #include "nsIPresentationSessionTransport.h"
13 #include "nsIPresentationSessionTransportBuilder.h"
14 #include "nsServiceManagerUtils.h"
15 #include "PresentationBuilderParent.h"
16 #include "PresentationParent.h"
17 #include "PresentationService.h"
18 #include "PresentationSessionInfo.h"
19
20 namespace mozilla {
21 namespace dom {
22
23 namespace {
24
25 class PresentationTransportBuilderConstructorIPC final
26 : public nsIPresentationTransportBuilderConstructor {
27 public:
28 NS_DECL_ISUPPORTS
29 NS_DECL_NSIPRESENTATIONTRANSPORTBUILDERCONSTRUCTOR
30
PresentationTransportBuilderConstructorIPC(PresentationParent * aParent)31 explicit PresentationTransportBuilderConstructorIPC(
32 PresentationParent* aParent)
33 : mParent(aParent) {}
34
35 private:
36 virtual ~PresentationTransportBuilderConstructorIPC() = default;
37
38 RefPtr<PresentationParent> mParent;
39 };
40
NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,nsIPresentationTransportBuilderConstructor)41 NS_IMPL_ISUPPORTS(PresentationTransportBuilderConstructorIPC,
42 nsIPresentationTransportBuilderConstructor)
43
44 NS_IMETHODIMP
45 PresentationTransportBuilderConstructorIPC::CreateTransportBuilder(
46 uint8_t aType, nsIPresentationSessionTransportBuilder** aRetval) {
47 if (NS_WARN_IF(!aRetval)) {
48 return NS_ERROR_INVALID_ARG;
49 }
50
51 *aRetval = nullptr;
52
53 if (NS_WARN_IF(aType != nsIPresentationChannelDescription::TYPE_TCP &&
54 aType !=
55 nsIPresentationChannelDescription::TYPE_DATACHANNEL)) {
56 return NS_ERROR_INVALID_ARG;
57 }
58
59 if (XRE_IsContentProcess()) {
60 MOZ_ASSERT(false,
61 "CreateTransportBuilder can only be invoked in parent process.");
62 return NS_ERROR_FAILURE;
63 }
64
65 nsCOMPtr<nsIPresentationSessionTransportBuilder> builder;
66 if (aType == nsIPresentationChannelDescription::TYPE_TCP) {
67 builder = do_CreateInstance(PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID);
68 } else {
69 builder = new PresentationBuilderParent(mParent);
70 }
71
72 if (NS_WARN_IF(!builder)) {
73 return NS_ERROR_DOM_OPERATION_ERR;
74 }
75
76 builder.forget(aRetval);
77 return NS_OK;
78 }
79
80 } // anonymous namespace
81
82 /*
83 * Implementation of PresentationParent
84 */
85
86 NS_IMPL_ISUPPORTS(PresentationParent, nsIPresentationAvailabilityListener,
87 nsIPresentationSessionListener,
88 nsIPresentationRespondingListener)
89
90 PresentationParent::PresentationParent() = default;
91
92 /* virtual */ PresentationParent::~PresentationParent() = default;
93
Init(ContentParentId aContentParentId)94 bool PresentationParent::Init(ContentParentId aContentParentId) {
95 MOZ_ASSERT(!mService);
96 mService = do_GetService(PRESENTATION_SERVICE_CONTRACTID);
97 mChildId = aContentParentId;
98 return !NS_WARN_IF(!mService);
99 }
100
ActorDestroy(ActorDestroyReason aWhy)101 void PresentationParent::ActorDestroy(ActorDestroyReason aWhy) {
102 mActorDestroyed = true;
103
104 for (uint32_t i = 0; i < mSessionIdsAtController.Length(); i++) {
105 Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(
106 mSessionIdsAtController[i], nsIPresentationService::ROLE_CONTROLLER)));
107 }
108 mSessionIdsAtController.Clear();
109
110 for (uint32_t i = 0; i < mSessionIdsAtReceiver.Length(); i++) {
111 Unused << NS_WARN_IF(NS_FAILED(mService->UnregisterSessionListener(
112 mSessionIdsAtReceiver[i], nsIPresentationService::ROLE_RECEIVER)));
113 }
114 mSessionIdsAtReceiver.Clear();
115
116 for (uint32_t i = 0; i < mWindowIds.Length(); i++) {
117 Unused << NS_WARN_IF(
118 NS_FAILED(mService->UnregisterRespondingListener(mWindowIds[i])));
119 }
120 mWindowIds.Clear();
121
122 if (!mContentAvailabilityUrls.IsEmpty()) {
123 mService->UnregisterAvailabilityListener(mContentAvailabilityUrls, this);
124 }
125 mService = nullptr;
126 }
127
RecvPPresentationRequestConstructor(PPresentationRequestParent * aActor,const PresentationIPCRequest & aRequest)128 mozilla::ipc::IPCResult PresentationParent::RecvPPresentationRequestConstructor(
129 PPresentationRequestParent* aActor,
130 const PresentationIPCRequest& aRequest) {
131 PresentationRequestParent* actor =
132 static_cast<PresentationRequestParent*>(aActor);
133
134 nsresult rv = NS_ERROR_FAILURE;
135 switch (aRequest.type()) {
136 case PresentationIPCRequest::TStartSessionRequest:
137 rv = actor->DoRequest(aRequest.get_StartSessionRequest());
138 break;
139 case PresentationIPCRequest::TSendSessionMessageRequest:
140 rv = actor->DoRequest(aRequest.get_SendSessionMessageRequest());
141 break;
142 case PresentationIPCRequest::TCloseSessionRequest:
143 rv = actor->DoRequest(aRequest.get_CloseSessionRequest());
144 break;
145 case PresentationIPCRequest::TTerminateSessionRequest:
146 rv = actor->DoRequest(aRequest.get_TerminateSessionRequest());
147 break;
148 case PresentationIPCRequest::TReconnectSessionRequest:
149 rv = actor->DoRequest(aRequest.get_ReconnectSessionRequest());
150 break;
151 case PresentationIPCRequest::TBuildTransportRequest:
152 rv = actor->DoRequest(aRequest.get_BuildTransportRequest());
153 break;
154 default:
155 MOZ_CRASH("Unknown PresentationIPCRequest type");
156 }
157
158 if (NS_WARN_IF(NS_FAILED(rv))) {
159 return IPC_FAIL_NO_REASON(this);
160 }
161 return IPC_OK();
162 }
163
AllocPPresentationRequestParent(const PresentationIPCRequest & aRequest)164 PPresentationRequestParent* PresentationParent::AllocPPresentationRequestParent(
165 const PresentationIPCRequest& aRequest) {
166 MOZ_ASSERT(mService);
167 RefPtr<PresentationRequestParent> actor =
168 new PresentationRequestParent(mService, mChildId);
169 return actor.forget().take();
170 }
171
DeallocPPresentationRequestParent(PPresentationRequestParent * aActor)172 bool PresentationParent::DeallocPPresentationRequestParent(
173 PPresentationRequestParent* aActor) {
174 RefPtr<PresentationRequestParent> actor =
175 dont_AddRef(static_cast<PresentationRequestParent*>(aActor));
176 return true;
177 }
178
AllocPPresentationBuilderParent(const nsString & aSessionId,const uint8_t & aRole)179 PPresentationBuilderParent* PresentationParent::AllocPPresentationBuilderParent(
180 const nsString& aSessionId, const uint8_t& aRole) {
181 MOZ_ASSERT_UNREACHABLE(
182 "We should never be manually allocating "
183 "AllocPPresentationBuilderParent actors");
184 return nullptr;
185 }
186
DeallocPPresentationBuilderParent(PPresentationBuilderParent * aActor)187 bool PresentationParent::DeallocPPresentationBuilderParent(
188 PPresentationBuilderParent* aActor) {
189 return true;
190 }
191
Recv__delete__()192 mozilla::ipc::IPCResult PresentationParent::Recv__delete__() {
193 return IPC_OK();
194 }
195
RecvRegisterAvailabilityHandler(nsTArray<nsString> && aAvailabilityUrls)196 mozilla::ipc::IPCResult PresentationParent::RecvRegisterAvailabilityHandler(
197 nsTArray<nsString>&& aAvailabilityUrls) {
198 MOZ_ASSERT(mService);
199
200 Unused << NS_WARN_IF(NS_FAILED(
201 mService->RegisterAvailabilityListener(aAvailabilityUrls, this)));
202 mContentAvailabilityUrls.AppendElements(aAvailabilityUrls);
203 return IPC_OK();
204 }
205
RecvUnregisterAvailabilityHandler(nsTArray<nsString> && aAvailabilityUrls)206 mozilla::ipc::IPCResult PresentationParent::RecvUnregisterAvailabilityHandler(
207 nsTArray<nsString>&& aAvailabilityUrls) {
208 MOZ_ASSERT(mService);
209
210 Unused << NS_WARN_IF(NS_FAILED(
211 mService->UnregisterAvailabilityListener(aAvailabilityUrls, this)));
212 for (const auto& url : aAvailabilityUrls) {
213 mContentAvailabilityUrls.RemoveElement(url);
214 }
215 return IPC_OK();
216 }
217
218 /* virtual */ mozilla::ipc::IPCResult
RecvRegisterSessionHandler(const nsString & aSessionId,const uint8_t & aRole)219 PresentationParent::RecvRegisterSessionHandler(const nsString& aSessionId,
220 const uint8_t& aRole) {
221 MOZ_ASSERT(mService);
222
223 // Validate the accessibility (primarily for receiver side) so that a
224 // compromised child process can't fake the ID.
225 if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
226 ->IsSessionAccessible(aSessionId, aRole, OtherPid()))) {
227 return IPC_OK();
228 }
229
230 if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
231 mSessionIdsAtController.AppendElement(aSessionId);
232 } else {
233 mSessionIdsAtReceiver.AppendElement(aSessionId);
234 }
235 Unused << NS_WARN_IF(
236 NS_FAILED(mService->RegisterSessionListener(aSessionId, aRole, this)));
237 return IPC_OK();
238 }
239
240 /* virtual */ mozilla::ipc::IPCResult
RecvUnregisterSessionHandler(const nsString & aSessionId,const uint8_t & aRole)241 PresentationParent::RecvUnregisterSessionHandler(const nsString& aSessionId,
242 const uint8_t& aRole) {
243 MOZ_ASSERT(mService);
244 if (nsIPresentationService::ROLE_CONTROLLER == aRole) {
245 mSessionIdsAtController.RemoveElement(aSessionId);
246 } else {
247 mSessionIdsAtReceiver.RemoveElement(aSessionId);
248 }
249 Unused << NS_WARN_IF(
250 NS_FAILED(mService->UnregisterSessionListener(aSessionId, aRole)));
251 return IPC_OK();
252 }
253
254 /* virtual */ mozilla::ipc::IPCResult
RecvRegisterRespondingHandler(const uint64_t & aWindowId)255 PresentationParent::RecvRegisterRespondingHandler(const uint64_t& aWindowId) {
256 MOZ_ASSERT(mService);
257
258 mWindowIds.AppendElement(aWindowId);
259 Unused << NS_WARN_IF(
260 NS_FAILED(mService->RegisterRespondingListener(aWindowId, this)));
261 return IPC_OK();
262 }
263
264 /* virtual */ mozilla::ipc::IPCResult
RecvUnregisterRespondingHandler(const uint64_t & aWindowId)265 PresentationParent::RecvUnregisterRespondingHandler(const uint64_t& aWindowId) {
266 MOZ_ASSERT(mService);
267 mWindowIds.RemoveElement(aWindowId);
268 Unused << NS_WARN_IF(
269 NS_FAILED(mService->UnregisterRespondingListener(aWindowId)));
270 return IPC_OK();
271 }
272
273 NS_IMETHODIMP
NotifyAvailableChange(const nsTArray<nsString> & aAvailabilityUrls,bool aAvailable)274 PresentationParent::NotifyAvailableChange(
275 const nsTArray<nsString>& aAvailabilityUrls, bool aAvailable) {
276 if (NS_WARN_IF(mActorDestroyed ||
277 !SendNotifyAvailableChange(aAvailabilityUrls, aAvailable))) {
278 return NS_ERROR_FAILURE;
279 }
280 return NS_OK;
281 }
282
283 NS_IMETHODIMP
NotifyStateChange(const nsAString & aSessionId,uint16_t aState,nsresult aReason)284 PresentationParent::NotifyStateChange(const nsAString& aSessionId,
285 uint16_t aState, nsresult aReason) {
286 if (NS_WARN_IF(mActorDestroyed ||
287 !SendNotifySessionStateChange(nsString(aSessionId), aState,
288 aReason))) {
289 return NS_ERROR_FAILURE;
290 }
291 return NS_OK;
292 }
293
294 NS_IMETHODIMP
NotifyMessage(const nsAString & aSessionId,const nsACString & aData,bool aIsBinary)295 PresentationParent::NotifyMessage(const nsAString& aSessionId,
296 const nsACString& aData, bool aIsBinary) {
297 if (NS_WARN_IF(mActorDestroyed ||
298 !SendNotifyMessage(nsString(aSessionId), nsCString(aData),
299 aIsBinary))) {
300 return NS_ERROR_FAILURE;
301 }
302 return NS_OK;
303 }
304
305 NS_IMETHODIMP
NotifySessionConnect(uint64_t aWindowId,const nsAString & aSessionId)306 PresentationParent::NotifySessionConnect(uint64_t aWindowId,
307 const nsAString& aSessionId) {
308 if (NS_WARN_IF(mActorDestroyed ||
309 !SendNotifySessionConnect(aWindowId, nsString(aSessionId)))) {
310 return NS_ERROR_FAILURE;
311 }
312 return NS_OK;
313 }
314
RecvNotifyReceiverReady(const nsString & aSessionId,const uint64_t & aWindowId,const bool & aIsLoading)315 mozilla::ipc::IPCResult PresentationParent::RecvNotifyReceiverReady(
316 const nsString& aSessionId, const uint64_t& aWindowId,
317 const bool& aIsLoading) {
318 MOZ_ASSERT(mService);
319
320 nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
321 new PresentationTransportBuilderConstructorIPC(this);
322 Unused << NS_WARN_IF(NS_FAILED(mService->NotifyReceiverReady(
323 aSessionId, aWindowId, aIsLoading, constructor)));
324 return IPC_OK();
325 }
326
RecvNotifyTransportClosed(const nsString & aSessionId,const uint8_t & aRole,const nsresult & aReason)327 mozilla::ipc::IPCResult PresentationParent::RecvNotifyTransportClosed(
328 const nsString& aSessionId, const uint8_t& aRole, const nsresult& aReason) {
329 MOZ_ASSERT(mService);
330
331 Unused << NS_WARN_IF(
332 NS_FAILED(mService->NotifyTransportClosed(aSessionId, aRole, aReason)));
333 return IPC_OK();
334 }
335
336 /*
337 * Implementation of PresentationRequestParent
338 */
339
NS_IMPL_ISUPPORTS(PresentationRequestParent,nsIPresentationServiceCallback)340 NS_IMPL_ISUPPORTS(PresentationRequestParent, nsIPresentationServiceCallback)
341
342 PresentationRequestParent::PresentationRequestParent(
343 nsIPresentationService* aService, ContentParentId aContentParentId)
344 : mService(aService), mChildId(aContentParentId) {}
345
346 PresentationRequestParent::~PresentationRequestParent() = default;
347
ActorDestroy(ActorDestroyReason aWhy)348 void PresentationRequestParent::ActorDestroy(ActorDestroyReason aWhy) {
349 mActorDestroyed = true;
350 mService = nullptr;
351 }
352
DoRequest(const StartSessionRequest & aRequest)353 nsresult PresentationRequestParent::DoRequest(
354 const StartSessionRequest& aRequest) {
355 MOZ_ASSERT(mService);
356
357 mSessionId = aRequest.sessionId();
358
359 RefPtr<EventTarget> eventTarget;
360 ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
361 RefPtr<BrowserParent> tp = cpm->GetTopLevelBrowserParentByProcessAndTabId(
362 mChildId, aRequest.tabId());
363 if (tp) {
364 eventTarget = tp->GetOwnerElement();
365 }
366
367 RefPtr<PresentationParent> parent =
368 static_cast<PresentationParent*>(Manager());
369 nsCOMPtr<nsIPresentationTransportBuilderConstructor> constructor =
370 new PresentationTransportBuilderConstructorIPC(parent);
371 return mService->StartSession(aRequest.urls(), aRequest.sessionId(),
372 aRequest.origin(), aRequest.deviceId(),
373 aRequest.windowId(), eventTarget,
374 aRequest.principal(), this, constructor);
375 }
376
DoRequest(const SendSessionMessageRequest & aRequest)377 nsresult PresentationRequestParent::DoRequest(
378 const SendSessionMessageRequest& aRequest) {
379 MOZ_ASSERT(mService);
380
381 // Validate the accessibility (primarily for receiver side) so that a
382 // compromised child process can't fake the ID.
383 if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
384 ->IsSessionAccessible(aRequest.sessionId(),
385 aRequest.role(), OtherPid()))) {
386 return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
387 }
388
389 nsresult rv = mService->SendSessionMessage(aRequest.sessionId(),
390 aRequest.role(), aRequest.data());
391 if (NS_WARN_IF(NS_FAILED(rv))) {
392 return SendResponse(rv);
393 }
394 return SendResponse(NS_OK);
395 }
396
DoRequest(const CloseSessionRequest & aRequest)397 nsresult PresentationRequestParent::DoRequest(
398 const CloseSessionRequest& aRequest) {
399 MOZ_ASSERT(mService);
400
401 // Validate the accessibility (primarily for receiver side) so that a
402 // compromised child process can't fake the ID.
403 if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
404 ->IsSessionAccessible(aRequest.sessionId(),
405 aRequest.role(), OtherPid()))) {
406 return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
407 }
408
409 nsresult rv = mService->CloseSession(aRequest.sessionId(), aRequest.role(),
410 aRequest.closedReason());
411 if (NS_WARN_IF(NS_FAILED(rv))) {
412 return SendResponse(rv);
413 }
414 return SendResponse(NS_OK);
415 }
416
DoRequest(const TerminateSessionRequest & aRequest)417 nsresult PresentationRequestParent::DoRequest(
418 const TerminateSessionRequest& aRequest) {
419 MOZ_ASSERT(mService);
420
421 // Validate the accessibility (primarily for receiver side) so that a
422 // compromised child process can't fake the ID.
423 if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
424 ->IsSessionAccessible(aRequest.sessionId(),
425 aRequest.role(), OtherPid()))) {
426 return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
427 }
428
429 nsresult rv =
430 mService->TerminateSession(aRequest.sessionId(), aRequest.role());
431 if (NS_WARN_IF(NS_FAILED(rv))) {
432 return SendResponse(rv);
433 }
434 return SendResponse(NS_OK);
435 }
436
DoRequest(const ReconnectSessionRequest & aRequest)437 nsresult PresentationRequestParent::DoRequest(
438 const ReconnectSessionRequest& aRequest) {
439 MOZ_ASSERT(mService);
440
441 // Validate the accessibility (primarily for receiver side) so that a
442 // compromised child process can't fake the ID.
443 if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
444 ->IsSessionAccessible(aRequest.sessionId(),
445 aRequest.role(), OtherPid()))) {
446 // NOTE: Return NS_ERROR_DOM_NOT_FOUND_ERR here to match the spec.
447 // https://w3c.github.io/presentation-api/#reconnecting-to-a-presentation
448 return SendResponse(NS_ERROR_DOM_NOT_FOUND_ERR);
449 }
450
451 mSessionId = aRequest.sessionId();
452 return mService->ReconnectSession(aRequest.urls(), aRequest.sessionId(),
453 aRequest.role(), this);
454 }
455
DoRequest(const BuildTransportRequest & aRequest)456 nsresult PresentationRequestParent::DoRequest(
457 const BuildTransportRequest& aRequest) {
458 MOZ_ASSERT(mService);
459
460 // Validate the accessibility (primarily for receiver side) so that a
461 // compromised child process can't fake the ID.
462 if (NS_WARN_IF(!static_cast<PresentationService*>(mService.get())
463 ->IsSessionAccessible(aRequest.sessionId(),
464 aRequest.role(), OtherPid()))) {
465 return SendResponse(NS_ERROR_DOM_SECURITY_ERR);
466 }
467
468 nsresult rv = mService->BuildTransport(aRequest.sessionId(), aRequest.role());
469 if (NS_WARN_IF(NS_FAILED(rv))) {
470 return SendResponse(rv);
471 }
472 return SendResponse(NS_OK);
473 }
474
475 NS_IMETHODIMP
NotifySuccess(const nsAString & aUrl)476 PresentationRequestParent::NotifySuccess(const nsAString& aUrl) {
477 Unused << SendNotifyRequestUrlSelected(nsString(aUrl));
478 return SendResponse(NS_OK);
479 }
480
481 NS_IMETHODIMP
NotifyError(nsresult aError)482 PresentationRequestParent::NotifyError(nsresult aError) {
483 return SendResponse(aError);
484 }
485
SendResponse(nsresult aResult)486 nsresult PresentationRequestParent::SendResponse(nsresult aResult) {
487 if (NS_WARN_IF(mActorDestroyed || !Send__delete__(this, aResult))) {
488 return NS_ERROR_FAILURE;
489 }
490
491 return NS_OK;
492 }
493
494 } // namespace dom
495 } // namespace mozilla
496