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 "VRManagerChild.h"
8 
9 #include "VRLayerChild.h"
10 #include "VRManagerParent.h"
11 #include "VRThread.h"
12 #include "VRDisplayClient.h"
13 #include "nsGlobalWindow.h"
14 #include "mozilla/ProfilerMarkers.h"
15 #include "mozilla/StaticPtr.h"
16 #include "mozilla/layers/CompositorThread.h"  // for CompositorThread
17 #include "mozilla/dom/Navigator.h"
18 #include "mozilla/dom/VREventObserver.h"
19 #include "mozilla/dom/WebXRBinding.h"
20 #include "mozilla/dom/WindowBinding.h"  // for FrameRequestCallback
21 #include "mozilla/dom/XRSystem.h"
22 #include "mozilla/dom/XRFrame.h"
23 #include "mozilla/dom/ContentChild.h"
24 #include "nsContentUtils.h"
25 #include "mozilla/dom/GamepadManager.h"
26 #include "mozilla/ipc/Endpoint.h"
27 #include "mozilla/layers/SyncObject.h"
28 #include "mozilla/layers/TextureForwarder.h"
29 
30 using namespace mozilla::dom;
31 
32 namespace {
33 const nsTArray<RefPtr<mozilla::gfx::VRManagerEventObserver>>::index_type
34     kNoIndex = nsTArray<RefPtr<mozilla::gfx::VRManagerEventObserver>>::NoIndex;
35 }  // namespace
36 
37 namespace mozilla {
38 namespace gfx {
39 
40 static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton;
41 static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton;
42 
43 static TimeStamp sMostRecentFrameEnd;
44 static TimeDuration sAverageFrameInterval;
45 
ReleaseVRManagerParentSingleton()46 void ReleaseVRManagerParentSingleton() { sVRManagerParentSingleton = nullptr; }
47 
VRManagerChild()48 VRManagerChild::VRManagerChild()
49     : mRuntimeCapabilities(VRDisplayCapabilityFlags::Cap_None),
50       mFrameRequestCallbackCounter(0),
51       mWaitingForEnumeration(false),
52       mBackend(layers::LayersBackend::LAYERS_NONE) {
53   MOZ_ASSERT(NS_IsMainThread());
54 
55   mStartTimeStamp = TimeStamp::Now();
56   AddRef();
57 }
58 
~VRManagerChild()59 VRManagerChild::~VRManagerChild() { MOZ_ASSERT(NS_IsMainThread()); }
60 
61 /*static*/
IdentifyTextureHost(const TextureFactoryIdentifier & aIdentifier)62 void VRManagerChild::IdentifyTextureHost(
63     const TextureFactoryIdentifier& aIdentifier) {
64   if (sVRManagerChildSingleton) {
65     sVRManagerChildSingleton->mBackend = aIdentifier.mParentBackend;
66   }
67 }
68 
GetBackendType() const69 layers::LayersBackend VRManagerChild::GetBackendType() const {
70   return mBackend;
71 }
72 
73 /*static*/
Get()74 VRManagerChild* VRManagerChild::Get() {
75   MOZ_ASSERT(sVRManagerChildSingleton);
76   return sVRManagerChildSingleton;
77 }
78 
79 /* static */
IsCreated()80 bool VRManagerChild::IsCreated() { return !!sVRManagerChildSingleton; }
81 
82 /* static */
IsPresenting()83 bool VRManagerChild::IsPresenting() {
84   if (!VRManagerChild::IsCreated()) {
85     return false;
86   }
87 
88   nsTArray<RefPtr<VRDisplayClient>> displays;
89   sVRManagerChildSingleton->GetVRDisplays(displays);
90 
91   bool result = false;
92   for (auto& display : displays) {
93     result |= display->IsPresenting();
94   }
95   return result;
96 }
97 
GetIdleDeadlineHint(TimeStamp aDefault)98 TimeStamp VRManagerChild::GetIdleDeadlineHint(TimeStamp aDefault) {
99   MOZ_ASSERT(NS_IsMainThread());
100   if (!VRManagerChild::IsCreated() || sMostRecentFrameEnd.IsNull()) {
101     return aDefault;
102   }
103 
104   TimeStamp idleEnd = sMostRecentFrameEnd + sAverageFrameInterval;
105   return idleEnd < aDefault ? idleEnd : aDefault;
106 }
107 
108 /* static */
InitForContent(Endpoint<PVRManagerChild> && aEndpoint)109 bool VRManagerChild::InitForContent(Endpoint<PVRManagerChild>&& aEndpoint) {
110   MOZ_ASSERT(NS_IsMainThread());
111 
112   RefPtr<VRManagerChild> child(new VRManagerChild());
113   if (!aEndpoint.Bind(child)) {
114     return false;
115   }
116   sVRManagerChildSingleton = child;
117   return true;
118 }
119 
120 /*static*/
InitSameProcess()121 void VRManagerChild::InitSameProcess() {
122   MOZ_ASSERT(NS_IsMainThread());
123   MOZ_ASSERT(!sVRManagerChildSingleton);
124 
125   sVRManagerChildSingleton = new VRManagerChild();
126   sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
127   sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(),
128                                  CompositorThread(), mozilla::ipc::ChildSide);
129 }
130 
131 /* static */
InitWithGPUProcess(Endpoint<PVRManagerChild> && aEndpoint)132 void VRManagerChild::InitWithGPUProcess(Endpoint<PVRManagerChild>&& aEndpoint) {
133   MOZ_ASSERT(NS_IsMainThread());
134   MOZ_ASSERT(!sVRManagerChildSingleton);
135 
136   sVRManagerChildSingleton = new VRManagerChild();
137   if (!aEndpoint.Bind(sVRManagerChildSingleton)) {
138     MOZ_CRASH("Couldn't Open() Compositor channel.");
139   }
140 }
141 
142 /*static*/
ShutDown()143 void VRManagerChild::ShutDown() {
144   MOZ_ASSERT(NS_IsMainThread());
145   if (!sVRManagerChildSingleton) {
146     return;
147   }
148   sVRManagerChildSingleton->Close();
149   sVRManagerChildSingleton = nullptr;
150 }
151 
ActorDealloc()152 void VRManagerChild::ActorDealloc() { Release(); }
153 
ActorDestroy(ActorDestroyReason aReason)154 void VRManagerChild::ActorDestroy(ActorDestroyReason aReason) {
155   if (sVRManagerChildSingleton == this) {
156     sVRManagerChildSingleton = nullptr;
157   }
158 }
159 
AllocPVRLayerChild(const uint32_t & aDisplayID,const uint32_t & aGroup)160 PVRLayerChild* VRManagerChild::AllocPVRLayerChild(const uint32_t& aDisplayID,
161                                                   const uint32_t& aGroup) {
162   return VRLayerChild::CreateIPDLActor();
163 }
164 
DeallocPVRLayerChild(PVRLayerChild * actor)165 bool VRManagerChild::DeallocPVRLayerChild(PVRLayerChild* actor) {
166   return VRLayerChild::DestroyIPDLActor(actor);
167 }
168 
UpdateDisplayInfo(const VRDisplayInfo & aDisplayInfo)169 void VRManagerChild::UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo) {
170   nsTArray<uint32_t> disconnectedDisplays;
171   nsTArray<uint32_t> connectedDisplays;
172 
173   const nsTArray<RefPtr<VRDisplayClient>> prevDisplays(mDisplays.Clone());
174 
175   // Check if any displays have been disconnected
176   for (auto& display : prevDisplays) {
177     bool found = false;
178     if (aDisplayInfo.GetDisplayID() != 0) {
179       if (display->GetDisplayInfo().GetDisplayID() ==
180           aDisplayInfo.GetDisplayID()) {
181         found = true;
182         break;
183       }
184     }
185     if (!found) {
186       // In order to make the current VRDisplay can continue to apply for the
187       // newest VRDisplayInfo, we need to exit presentionation before
188       // disconnecting.
189       if (display->IsPresentationGenerationCurrent()) {
190         NotifyPresentationGenerationChangedInternal(
191             display->GetDisplayInfo().GetDisplayID());
192 
193         RefPtr<VRManagerChild> vm = VRManagerChild::Get();
194         vm->FireDOMVRDisplayPresentChangeEvent(
195             display->GetDisplayInfo().GetDisplayID());
196       }
197       display->NotifyDisconnected();
198       disconnectedDisplays.AppendElement(
199           display->GetDisplayInfo().GetDisplayID());
200     }
201   }
202 
203   // mDisplays could be a hashed container for more scalability, but not worth
204   // it now as we expect < 10 entries.
205   nsTArray<RefPtr<VRDisplayClient>> displays;
206   if (aDisplayInfo.GetDisplayID() != 0) {
207     bool isNewDisplay = true;
208     for (auto& display : prevDisplays) {
209       const VRDisplayInfo& prevInfo = display->GetDisplayInfo();
210       if (prevInfo.GetDisplayID() == aDisplayInfo.GetDisplayID()) {
211         if (aDisplayInfo.GetIsConnected() && !prevInfo.GetIsConnected()) {
212           connectedDisplays.AppendElement(aDisplayInfo.GetDisplayID());
213         }
214         if (!aDisplayInfo.GetIsConnected() && prevInfo.GetIsConnected()) {
215           disconnectedDisplays.AppendElement(aDisplayInfo.GetDisplayID());
216         }
217         // MOZ_KnownLive because 'prevDisplays' is guaranteed to keep it alive.
218         //
219         // This can go away once
220         // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
221         MOZ_KnownLive(display)->UpdateDisplayInfo(aDisplayInfo);
222         displays.AppendElement(display);
223         isNewDisplay = false;
224         break;
225       }
226     }
227     if (isNewDisplay) {
228       displays.AppendElement(new VRDisplayClient(aDisplayInfo));
229       connectedDisplays.AppendElement(aDisplayInfo.GetDisplayID());
230     }
231   }
232 
233   mDisplays = std::move(displays);
234 
235   // We wish to fire the events only after mDisplays is updated
236   for (uint32_t displayID : disconnectedDisplays) {
237     FireDOMVRDisplayDisconnectEvent(displayID);
238   }
239 
240   for (uint32_t displayID : connectedDisplays) {
241     FireDOMVRDisplayConnectEvent(displayID);
242   }
243 }
244 
RuntimeSupportsVR() const245 bool VRManagerChild::RuntimeSupportsVR() const {
246   return bool(mRuntimeCapabilities & VRDisplayCapabilityFlags::Cap_ImmersiveVR);
247 }
RuntimeSupportsAR() const248 bool VRManagerChild::RuntimeSupportsAR() const {
249   return bool(mRuntimeCapabilities & VRDisplayCapabilityFlags::Cap_ImmersiveAR);
250 }
RuntimeSupportsInline() const251 bool VRManagerChild::RuntimeSupportsInline() const {
252   return bool(mRuntimeCapabilities & VRDisplayCapabilityFlags::Cap_Inline);
253 }
254 
RecvUpdateRuntimeCapabilities(const VRDisplayCapabilityFlags & aCapabilities)255 mozilla::ipc::IPCResult VRManagerChild::RecvUpdateRuntimeCapabilities(
256     const VRDisplayCapabilityFlags& aCapabilities) {
257   mRuntimeCapabilities = aCapabilities;
258   nsContentUtils::AddScriptRunner(NewRunnableMethod<>(
259       "gfx::VRManagerChild::NotifyRuntimeCapabilitiesUpdatedInternal", this,
260       &VRManagerChild::NotifyRuntimeCapabilitiesUpdatedInternal));
261   return IPC_OK();
262 }
263 
NotifyRuntimeCapabilitiesUpdatedInternal()264 void VRManagerChild::NotifyRuntimeCapabilitiesUpdatedInternal() {
265   const nsTArray<RefPtr<VRManagerEventObserver>> listeners = mListeners.Clone();
266   for (auto& listener : listeners) {
267     listener->NotifyDetectRuntimesCompleted();
268   }
269 }
270 
RecvUpdateDisplayInfo(const VRDisplayInfo & aDisplayInfo)271 mozilla::ipc::IPCResult VRManagerChild::RecvUpdateDisplayInfo(
272     const VRDisplayInfo& aDisplayInfo) {
273   UpdateDisplayInfo(aDisplayInfo);
274   for (auto& windowId : mNavigatorCallbacks) {
275     /** We must call NotifyVRDisplaysUpdated for every
276      * window's Navigator in mNavigatorCallbacks to ensure that
277      * the promise returned by Navigator.GetVRDevices
278      * can resolve.  This must happen even if no changes
279      * to VRDisplays have been detected here.
280      */
281     nsGlobalWindowInner* window =
282         nsGlobalWindowInner::GetInnerWindowWithId(windowId);
283     if (!window) {
284       continue;
285     }
286     dom::Navigator* nav = window->Navigator();
287     if (!nav) {
288       continue;
289     }
290     nav->NotifyVRDisplaysUpdated();
291   }
292   mNavigatorCallbacks.Clear();
293   if (mWaitingForEnumeration) {
294     nsContentUtils::AddScriptRunner(NewRunnableMethod<>(
295         "gfx::VRManagerChild::NotifyEnumerationCompletedInternal", this,
296         &VRManagerChild::NotifyEnumerationCompletedInternal));
297     mWaitingForEnumeration = false;
298   }
299   return IPC_OK();
300 }
301 
RecvNotifyPuppetCommandBufferCompleted(bool aSuccess)302 mozilla::ipc::IPCResult VRManagerChild::RecvNotifyPuppetCommandBufferCompleted(
303     bool aSuccess) {
304   RefPtr<dom::Promise> promise = mRunPuppetPromise;
305   mRunPuppetPromise = nullptr;
306   if (aSuccess) {
307     promise->MaybeResolve(JS::UndefinedHandleValue);
308   } else {
309     promise->MaybeRejectWithUndefined();
310   }
311   return IPC_OK();
312 }
313 
RecvNotifyPuppetResetComplete()314 mozilla::ipc::IPCResult VRManagerChild::RecvNotifyPuppetResetComplete() {
315   nsTArray<RefPtr<dom::Promise>> promises;
316   promises.AppendElements(mResetPuppetPromises);
317   mResetPuppetPromises.Clear();
318   for (const auto& promise : promises) {
319     promise->MaybeResolve(JS::UndefinedHandleValue);
320   }
321   return IPC_OK();
322 }
323 
RunPuppet(const nsTArray<uint64_t> & aBuffer,dom::Promise * aPromise,ErrorResult & aRv)324 void VRManagerChild::RunPuppet(const nsTArray<uint64_t>& aBuffer,
325                                dom::Promise* aPromise, ErrorResult& aRv) {
326   if (mRunPuppetPromise) {
327     // We only allow one puppet script to run simultaneously.
328     // The prior promise must be resolved before running a new
329     // script.
330     aRv.Throw(NS_ERROR_INVALID_ARG);
331     return;
332   }
333   if (!SendRunPuppet(aBuffer)) {
334     aRv.Throw(NS_ERROR_FAILURE);
335     return;
336   }
337   mRunPuppetPromise = aPromise;
338 }
339 
ResetPuppet(dom::Promise * aPromise,ErrorResult & aRv)340 void VRManagerChild::ResetPuppet(dom::Promise* aPromise, ErrorResult& aRv) {
341   if (!SendResetPuppet()) {
342     aRv.Throw(NS_ERROR_FAILURE);
343     return;
344   }
345   mResetPuppetPromises.AppendElement(aPromise);
346 }
347 
GetVRDisplays(nsTArray<RefPtr<VRDisplayClient>> & aDisplays)348 void VRManagerChild::GetVRDisplays(
349     nsTArray<RefPtr<VRDisplayClient>>& aDisplays) {
350   aDisplays = mDisplays.Clone();
351 }
352 
RefreshVRDisplaysWithCallback(uint64_t aWindowId)353 bool VRManagerChild::RefreshVRDisplaysWithCallback(uint64_t aWindowId) {
354   bool success = SendRefreshDisplays();
355   if (success) {
356     mNavigatorCallbacks.AppendElement(aWindowId);
357   }
358   return success;
359 }
360 
EnumerateVRDisplays()361 bool VRManagerChild::EnumerateVRDisplays() {
362   bool success = SendRefreshDisplays();
363   if (success) {
364     mWaitingForEnumeration = true;
365   }
366   return success;
367 }
368 
DetectRuntimes()369 void VRManagerChild::DetectRuntimes() { Unused << SendDetectRuntimes(); }
370 
CreateVRLayer(uint32_t aDisplayID,nsISerialEventTarget * aTarget,uint32_t aGroup)371 PVRLayerChild* VRManagerChild::CreateVRLayer(uint32_t aDisplayID,
372                                              nsISerialEventTarget* aTarget,
373                                              uint32_t aGroup) {
374   PVRLayerChild* vrLayerChild = AllocPVRLayerChild(aDisplayID, aGroup);
375   // Do the DOM labeling.
376   if (aTarget) {
377     SetEventTargetForActor(vrLayerChild, aTarget);
378     MOZ_ASSERT(vrLayerChild->GetActorEventTarget());
379   }
380   return SendPVRLayerConstructor(vrLayerChild, aDisplayID, aGroup);
381 }
382 
Call(const DOMHighResTimeStamp & aTimeStamp)383 void VRManagerChild::XRFrameRequest::Call(
384     const DOMHighResTimeStamp& aTimeStamp) {
385   if (mCallback) {
386     RefPtr<mozilla::dom::FrameRequestCallback> callback = mCallback;
387     callback->Call(aTimeStamp);
388   } else {
389     RefPtr<mozilla::dom::XRFrameRequestCallback> callback = mXRCallback;
390     RefPtr<mozilla::dom::XRFrame> frame = mXRFrame;
391     callback->Call(aTimeStamp, *frame);
392   }
393 }
394 
ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback & aCallback,int32_t * aHandle)395 nsresult VRManagerChild::ScheduleFrameRequestCallback(
396     mozilla::dom::FrameRequestCallback& aCallback, int32_t* aHandle) {
397   if (mFrameRequestCallbackCounter == INT32_MAX) {
398     // Can't increment without overflowing; bail out
399     return NS_ERROR_NOT_AVAILABLE;
400   }
401   int32_t newHandle = ++mFrameRequestCallbackCounter;
402 
403   mFrameRequestCallbacks.AppendElement(XRFrameRequest(aCallback, newHandle));
404 
405   *aHandle = newHandle;
406   return NS_OK;
407 }
408 
CancelFrameRequestCallback(int32_t aHandle)409 void VRManagerChild::CancelFrameRequestCallback(int32_t aHandle) {
410   // mFrameRequestCallbacks is stored sorted by handle
411   mFrameRequestCallbacks.RemoveElementSorted(aHandle);
412 }
413 
RunFrameRequestCallbacks()414 void VRManagerChild::RunFrameRequestCallbacks() {
415   AUTO_PROFILER_TRACING_MARKER("VR", "RunFrameRequestCallbacks", GRAPHICS);
416 
417   TimeStamp nowTime = TimeStamp::Now();
418   mozilla::TimeDuration duration = nowTime - mStartTimeStamp;
419   DOMHighResTimeStamp timeStamp = duration.ToMilliseconds();
420 
421   if (!sMostRecentFrameEnd.IsNull()) {
422     TimeDuration frameInterval = nowTime - sMostRecentFrameEnd;
423     if (sAverageFrameInterval.IsZero()) {
424       sAverageFrameInterval = frameInterval;
425     } else {
426       // Calculate the average interval between frame end and next frame start.
427       // Apply some smoothing to make it more stable.
428       const double smooth = 0.9;
429       sAverageFrameInterval = sAverageFrameInterval.MultDouble(smooth) +
430                               frameInterval.MultDouble(1.0 - smooth);
431     }
432   }
433 
434   nsTArray<XRFrameRequest> callbacks;
435   callbacks.AppendElements(mFrameRequestCallbacks);
436   mFrameRequestCallbacks.Clear();
437   for (auto& callback : callbacks) {
438     // The FrameRequest copied into the on-stack array holds a strong ref to its
439     // mCallback and there's nothing that can drop that ref until we return.
440     MOZ_KnownLive(callback.mCallback)->Call(timeStamp);
441   }
442 
443   if (IsPresenting()) {
444     sMostRecentFrameEnd = TimeStamp::Now();
445   }
446 }
447 
NotifyPresentationGenerationChanged(uint32_t aDisplayID)448 void VRManagerChild::NotifyPresentationGenerationChanged(uint32_t aDisplayID) {
449   nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
450       "gfx::VRManagerChild::NotifyPresentationGenerationChangedInternal", this,
451       &VRManagerChild::NotifyPresentationGenerationChangedInternal,
452       aDisplayID));
453 }
454 
FireDOMVRDisplayMountedEvent(uint32_t aDisplayID)455 void VRManagerChild::FireDOMVRDisplayMountedEvent(uint32_t aDisplayID) {
456   nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
457       "gfx::VRManagerChild::FireDOMVRDisplayMountedEventInternal", this,
458       &VRManagerChild::FireDOMVRDisplayMountedEventInternal, aDisplayID));
459 }
460 
FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID)461 void VRManagerChild::FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID) {
462   nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
463       "gfx::VRManagerChild::FireDOMVRDisplayUnmountedEventInternal", this,
464       &VRManagerChild::FireDOMVRDisplayUnmountedEventInternal, aDisplayID));
465 }
466 
FireDOMVRDisplayConnectEvent(uint32_t aDisplayID)467 void VRManagerChild::FireDOMVRDisplayConnectEvent(uint32_t aDisplayID) {
468   nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
469       "gfx::VRManagerChild::FireDOMVRDisplayConnectEventInternal", this,
470       &VRManagerChild::FireDOMVRDisplayConnectEventInternal, aDisplayID));
471 }
472 
FireDOMVRDisplayDisconnectEvent(uint32_t aDisplayID)473 void VRManagerChild::FireDOMVRDisplayDisconnectEvent(uint32_t aDisplayID) {
474   nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
475       "gfx::VRManagerChild::FireDOMVRDisplayDisconnectEventInternal", this,
476       &VRManagerChild::FireDOMVRDisplayDisconnectEventInternal, aDisplayID));
477 }
478 
FireDOMVRDisplayPresentChangeEvent(uint32_t aDisplayID)479 void VRManagerChild::FireDOMVRDisplayPresentChangeEvent(uint32_t aDisplayID) {
480   nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(
481       "gfx::VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal", this,
482       &VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal, aDisplayID));
483 
484   if (!IsPresenting()) {
485     sMostRecentFrameEnd = TimeStamp();
486     sAverageFrameInterval = 0;
487   }
488 }
489 
FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID)490 void VRManagerChild::FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID) {
491   // Iterate over a copy of mListeners, as dispatched events may modify it.
492   for (auto& listener : mListeners.Clone()) {
493     listener->NotifyVRDisplayMounted(aDisplayID);
494   }
495 }
496 
FireDOMVRDisplayUnmountedEventInternal(uint32_t aDisplayID)497 void VRManagerChild::FireDOMVRDisplayUnmountedEventInternal(
498     uint32_t aDisplayID) {
499   // Iterate over a copy of mListeners, as dispatched events may modify it.
500   for (auto& listener : mListeners.Clone()) {
501     listener->NotifyVRDisplayUnmounted(aDisplayID);
502   }
503 }
504 
FireDOMVRDisplayConnectEventInternal(uint32_t aDisplayID)505 void VRManagerChild::FireDOMVRDisplayConnectEventInternal(uint32_t aDisplayID) {
506   // Iterate over a copy of mListeners, as dispatched events may modify it.
507   for (auto& listener : mListeners.Clone()) {
508     listener->NotifyVRDisplayConnect(aDisplayID);
509   }
510 }
511 
FireDOMVRDisplayDisconnectEventInternal(uint32_t aDisplayID)512 void VRManagerChild::FireDOMVRDisplayDisconnectEventInternal(
513     uint32_t aDisplayID) {
514   // Iterate over a copy of mListeners, as dispatched events may modify it.
515   for (auto& listener : mListeners.Clone()) {
516     listener->NotifyVRDisplayDisconnect(aDisplayID);
517   }
518 }
519 
FireDOMVRDisplayPresentChangeEventInternal(uint32_t aDisplayID)520 void VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal(
521     uint32_t aDisplayID) {
522   // Iterate over a copy of mListeners, as dispatched events may modify it.
523   for (auto& listener : mListeners.Clone()) {
524     // MOZ_KnownLive because 'listeners' is guaranteed to keep it alive.
525     //
526     // This can go away once
527     // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
528     MOZ_KnownLive(listener)->NotifyVRDisplayPresentChange(aDisplayID);
529   }
530 }
531 
FireDOMVRDisplayConnectEventsForLoadInternal(uint32_t aDisplayID,VRManagerEventObserver * aObserver)532 void VRManagerChild::FireDOMVRDisplayConnectEventsForLoadInternal(
533     uint32_t aDisplayID, VRManagerEventObserver* aObserver) {
534   aObserver->NotifyVRDisplayConnect(aDisplayID);
535 }
536 
NotifyPresentationGenerationChangedInternal(uint32_t aDisplayID)537 void VRManagerChild::NotifyPresentationGenerationChangedInternal(
538     uint32_t aDisplayID) {
539   for (auto& listener : mListeners.Clone()) {
540     listener->NotifyPresentationGenerationChanged(aDisplayID);
541   }
542 }
543 
NotifyEnumerationCompletedInternal()544 void VRManagerChild::NotifyEnumerationCompletedInternal() {
545   for (auto& listener : mListeners.Clone()) {
546     listener->NotifyEnumerationCompleted();
547   }
548 }
549 
FireDOMVRDisplayConnectEventsForLoad(VRManagerEventObserver * aObserver)550 void VRManagerChild::FireDOMVRDisplayConnectEventsForLoad(
551     VRManagerEventObserver* aObserver) {
552   // We need to fire the VRDisplayConnect event when a page is loaded
553   // for each VR Display that has already been enumerated
554   for (const auto& display : mDisplays.Clone()) {
555     const VRDisplayInfo& info = display->GetDisplayInfo();
556     if (info.GetIsConnected()) {
557       nsContentUtils::AddScriptRunner(NewRunnableMethod<
558                                       uint32_t, RefPtr<VRManagerEventObserver>>(
559           "gfx::VRManagerChild::FireDOMVRDisplayConnectEventsForLoadInternal",
560           this, &VRManagerChild::FireDOMVRDisplayConnectEventsForLoadInternal,
561           info.GetDisplayID(), aObserver));
562     }
563   }
564 }
565 
AddListener(VRManagerEventObserver * aObserver)566 void VRManagerChild::AddListener(VRManagerEventObserver* aObserver) {
567   MOZ_ASSERT(aObserver);
568 
569   if (mListeners.IndexOf(aObserver) != kNoIndex) {
570     return;  // already exists
571   }
572 
573   mListeners.AppendElement(aObserver);
574   if (mListeners.Length() == 1) {
575     Unused << SendSetHaveEventListener(true);
576   }
577 }
578 
RemoveListener(VRManagerEventObserver * aObserver)579 void VRManagerChild::RemoveListener(VRManagerEventObserver* aObserver) {
580   MOZ_ASSERT(aObserver);
581 
582   mListeners.RemoveElement(aObserver);
583   if (mListeners.IsEmpty()) {
584     Unused << SendSetHaveEventListener(false);
585   }
586 }
587 
StartActivity()588 void VRManagerChild::StartActivity() { Unused << SendStartActivity(); }
589 
StopActivity()590 void VRManagerChild::StopActivity() {
591   for (auto& listener : mListeners) {
592     if (!listener->GetStopActivityStatus()) {
593       // We are still showing VR in the active window.
594       return;
595     }
596   }
597 
598   Unused << SendStopActivity();
599 }
600 
HandleFatalError(const char * aMsg) const601 void VRManagerChild::HandleFatalError(const char* aMsg) const {
602   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
603 }
604 
AddPromise(const uint32_t & aID,dom::Promise * aPromise)605 void VRManagerChild::AddPromise(const uint32_t& aID, dom::Promise* aPromise) {
606   MOZ_ASSERT(!mGamepadPromiseList.Contains(aID));
607   mGamepadPromiseList.InsertOrUpdate(aID, RefPtr{aPromise});
608 }
609 
GetVRAPIMode(uint32_t aDisplayID) const610 gfx::VRAPIMode VRManagerChild::GetVRAPIMode(uint32_t aDisplayID) const {
611   for (auto& display : mDisplays) {
612     if (display->GetDisplayInfo().GetDisplayID() == aDisplayID) {
613       return display->GetXRAPIMode();
614     }
615   }
616   return VRAPIMode::WebXR;
617 }
618 
RecvReplyGamepadVibrateHaptic(const uint32_t & aPromiseID)619 mozilla::ipc::IPCResult VRManagerChild::RecvReplyGamepadVibrateHaptic(
620     const uint32_t& aPromiseID) {
621   // VRManagerChild could be at other processes, but GamepadManager
622   // only exists at the content process or the same process
623   // in non-e10s mode.
624   MOZ_ASSERT(XRE_IsContentProcess() || IsSameProcess());
625 
626   RefPtr<dom::Promise> p;
627   if (!mGamepadPromiseList.Get(aPromiseID, getter_AddRefs(p))) {
628     MOZ_CRASH("We should always have a promise.");
629   }
630 
631   p->MaybeResolve(true);
632   mGamepadPromiseList.Remove(aPromiseID);
633   return IPC_OK();
634 }
635 
636 }  // namespace gfx
637 }  // namespace mozilla
638