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