tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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