tor-browser

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

VRDisplayClient.cpp (27708B)


      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 <math.h>
      8 
      9 #include "prlink.h"
     10 #include "prenv.h"
     11 
     12 #include "nsIGlobalObject.h"
     13 #include "nsRefPtrHashtable.h"
     14 #include "nsString.h"
     15 #include "mozilla/dom/GamepadHandle.h"
     16 #include "mozilla/dom/GamepadManager.h"
     17 #include "mozilla/dom/Gamepad.h"
     18 #include "mozilla/dom/XRSession.h"
     19 #include "mozilla/dom/XRInputSourceArray.h"
     20 #include "mozilla/Preferences.h"
     21 #include "mozilla/StaticPrefs_dom.h"
     22 #include "mozilla/dom/WebXRBinding.h"
     23 #include "nsServiceManagerUtils.h"
     24 
     25 #ifdef XP_WIN
     26 #  include "../layers/d3d11/CompositorD3D11.h"
     27 #endif
     28 
     29 #include "VRDisplayClient.h"
     30 #include "VRDisplayPresentation.h"
     31 #include "VRManagerChild.h"
     32 #include "VRLayerChild.h"
     33 
     34 using namespace mozilla;
     35 using namespace mozilla::gfx;
     36 
     37 using mozilla::dom::GamepadHandle;
     38 using mozilla::dom::GamepadHandleKind;
     39 
     40 VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
     41    : mDisplayInfo(aDisplayInfo),
     42      bLastEventWasMounted(false),
     43      bLastEventWasPresenting(false),
     44      mPresentationCount(0),
     45      mLastEventFrameId(0),
     46      mLastPresentingGeneration(0),
     47      mLastEventControllerState{},
     48      // For now WebVR is default to prevent a VRDisplay restore bug in WebVR
     49      // compatibility mode. See Bug 1630512
     50      mAPIMode(VRAPIMode::WebVR) {
     51  MOZ_COUNT_CTOR(VRDisplayClient);
     52 }
     53 
     54 VRDisplayClient::~VRDisplayClient() { MOZ_COUNT_DTOR(VRDisplayClient); }
     55 
     56 void VRDisplayClient::UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo) {
     57  mDisplayInfo = aDisplayInfo;
     58  FireEvents();
     59 }
     60 
     61 already_AddRefed<VRDisplayPresentation> VRDisplayClient::BeginPresentation(
     62    const nsTArray<mozilla::dom::VRLayer>& aLayers, uint32_t aGroup) {
     63  PresentationCreated();
     64  RefPtr<VRDisplayPresentation> presentation =
     65      new VRDisplayPresentation(this, aLayers, aGroup);
     66  return presentation.forget();
     67 }
     68 
     69 void VRDisplayClient::PresentationCreated() { ++mPresentationCount; }
     70 
     71 void VRDisplayClient::PresentationDestroyed() { --mPresentationCount; }
     72 
     73 void VRDisplayClient::SessionStarted(dom::XRSession* aSession) {
     74  PresentationCreated();
     75  MakePresentationGenerationCurrent();
     76  mSessions.AppendElement(aSession);
     77 }
     78 void VRDisplayClient::SessionEnded(dom::XRSession* aSession) {
     79  mSessions.RemoveElement(aSession);
     80  PresentationDestroyed();
     81 }
     82 
     83 void VRDisplayClient::StartFrame() {
     84  RefPtr<VRManagerChild> vm = VRManagerChild::Get();
     85  vm->RunFrameRequestCallbacks();
     86 
     87  nsTArray<RefPtr<dom::XRSession>> sessions;
     88  sessions.AppendElements(mSessions);
     89  for (auto session : sessions) {
     90    session->StartFrame();
     91  }
     92 }
     93 
     94 void VRDisplayClient::SetGroupMask(uint32_t aGroupMask) {
     95  VRManagerChild* vm = VRManagerChild::Get();
     96  vm->SendSetGroupMask(mDisplayInfo.mDisplayID, aGroupMask);
     97 }
     98 
     99 bool VRDisplayClient::IsPresentationGenerationCurrent() const {
    100  if (mLastPresentingGeneration !=
    101      mDisplayInfo.mDisplayState.presentingGeneration) {
    102    return false;
    103  }
    104 
    105  return true;
    106 }
    107 
    108 void VRDisplayClient::MakePresentationGenerationCurrent() {
    109  mLastPresentingGeneration = mDisplayInfo.mDisplayState.presentingGeneration;
    110 }
    111 
    112 gfx::VRAPIMode VRDisplayClient::GetXRAPIMode() const { return mAPIMode; }
    113 
    114 void VRDisplayClient::SetXRAPIMode(gfx::VRAPIMode aMode) { mAPIMode = aMode; }
    115 
    116 void VRDisplayClient::FireEvents() {
    117  RefPtr<VRManagerChild> vm = VRManagerChild::Get();
    118  // Only fire these events for non-chrome VR sessions
    119  bool isPresenting = (mDisplayInfo.mPresentingGroups & kVRGroupContent) != 0;
    120 
    121  // Check if we need to trigger onVRDisplayPresentChange event
    122  if (bLastEventWasPresenting != isPresenting) {
    123    bLastEventWasPresenting = isPresenting;
    124    vm->FireDOMVRDisplayPresentChangeEvent(mDisplayInfo.mDisplayID);
    125  }
    126 
    127  // Check if we need to trigger onvrdisplayactivate event
    128  if (!bLastEventWasMounted && mDisplayInfo.mDisplayState.isMounted) {
    129    bLastEventWasMounted = true;
    130    if (StaticPrefs::dom_vr_autoactivate_enabled()) {
    131      vm->FireDOMVRDisplayMountedEvent(mDisplayInfo.mDisplayID);
    132    }
    133  }
    134 
    135  // Check if we need to trigger onvrdisplaydeactivate event
    136  if (bLastEventWasMounted && !mDisplayInfo.mDisplayState.isMounted) {
    137    bLastEventWasMounted = false;
    138    if (StaticPrefs::dom_vr_autoactivate_enabled()) {
    139      vm->FireDOMVRDisplayUnmountedEvent(mDisplayInfo.mDisplayID);
    140    }
    141  }
    142 
    143  if (mLastPresentingGeneration !=
    144      mDisplayInfo.mDisplayState.presentingGeneration) {
    145    mLastPresentingGeneration = mDisplayInfo.mDisplayState.presentingGeneration;
    146    vm->NotifyPresentationGenerationChanged(mDisplayInfo.mDisplayID);
    147  }
    148 
    149  // In WebXR spec, Gamepad instances returned by an XRInputSource's gamepad
    150  // attribute MUST NOT be included in the array returned by
    151  // navigator.getGamepads().
    152  if (mAPIMode == VRAPIMode::WebVR) {
    153    FireGamepadEvents();
    154  }
    155  // Update controller states into XRInputSourceArray.
    156  for (auto& session : mSessions) {
    157    dom::XRInputSourceArray* inputs = session->InputSources();
    158    if (inputs) {
    159      inputs->Update(session);
    160    }
    161  }
    162 
    163  // Check if we need to trigger VRDisplay.requestAnimationFrame
    164  if (mLastEventFrameId != mDisplayInfo.mFrameId) {
    165    mLastEventFrameId = mDisplayInfo.mFrameId;
    166    StartFrame();
    167  }
    168 }
    169 
    170 void VRDisplayClient::GamepadMappingForWebVR(
    171    VRControllerState& aControllerState) {
    172  auto triggerValue = aControllerState.triggerValue;
    173  const uint64_t buttonPressed = aControllerState.buttonPressed;
    174  const uint64_t buttonTouched = aControllerState.buttonTouched;
    175 
    176  auto SetTriggerValue = [&](uint64_t newSlot, uint64_t oldSlot) {
    177    aControllerState.triggerValue[newSlot] = triggerValue[oldSlot];
    178  };
    179  auto ShiftButtonBitForNewSlot = [&](uint64_t newSlot, uint64_t oldSlot,
    180                                      bool aIsTouch = false) {
    181    if (aIsTouch) {
    182      return ((buttonTouched & (1ULL << oldSlot)) != 0) * (1ULL << newSlot);
    183    }
    184    SetTriggerValue(newSlot, oldSlot);
    185    return ((buttonPressed & (1ULL << oldSlot)) != 0) * (1ULL << newSlot);
    186  };
    187 
    188  switch (aControllerState.type) {
    189    case VRControllerType::HTCVive:
    190      aControllerState.buttonPressed =
    191          ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
    192          ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(3, 4);
    193      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 1, true) |
    194                                       ShiftButtonBitForNewSlot(2, 1, true) |
    195                                       ShiftButtonBitForNewSlot(0, 2, true) |
    196                                       ShiftButtonBitForNewSlot(3, 4, true);
    197      aControllerState.numButtons = 4;
    198      aControllerState.numAxes = 2;
    199      break;
    200    case VRControllerType::MSMR:
    201      aControllerState.buttonPressed =
    202          ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
    203          ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(3, 3) |
    204          ShiftButtonBitForNewSlot(4, 4);
    205      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(1, 0, true) |
    206                                       ShiftButtonBitForNewSlot(2, 1, true) |
    207                                       ShiftButtonBitForNewSlot(0, 2, true) |
    208                                       ShiftButtonBitForNewSlot(3, 3, true) |
    209                                       ShiftButtonBitForNewSlot(4, 4, true);
    210      aControllerState.numButtons = 5;
    211      aControllerState.numAxes = 4;
    212      break;
    213    case VRControllerType::HTCViveCosmos:
    214      aControllerState.buttonPressed =
    215          ShiftButtonBitForNewSlot(0, 0) | ShiftButtonBitForNewSlot(1, 1) |
    216          ShiftButtonBitForNewSlot(4, 3) | ShiftButtonBitForNewSlot(2, 4) |
    217          ShiftButtonBitForNewSlot(3, 5) | ShiftButtonBitForNewSlot(5, 6);
    218      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 0, true) |
    219                                       ShiftButtonBitForNewSlot(1, 1, true) |
    220                                       ShiftButtonBitForNewSlot(4, 3, true) |
    221                                       ShiftButtonBitForNewSlot(2, 4, true) |
    222                                       ShiftButtonBitForNewSlot(3, 5, true) |
    223                                       ShiftButtonBitForNewSlot(5, 6, true);
    224      aControllerState.axisValue[0] = aControllerState.axisValue[2];
    225      aControllerState.axisValue[1] = aControllerState.axisValue[3];
    226      aControllerState.numButtons = 6;
    227      aControllerState.numAxes = 2;
    228      break;
    229    case VRControllerType::HTCViveFocus:
    230      aControllerState.buttonPressed =
    231          ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
    232      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
    233                                       ShiftButtonBitForNewSlot(1, 0, true);
    234      aControllerState.numButtons = 2;
    235      aControllerState.numAxes = 2;
    236      break;
    237    case VRControllerType::HTCViveFocusPlus: {
    238      aControllerState.buttonPressed = ShiftButtonBitForNewSlot(0, 2) |
    239                                       ShiftButtonBitForNewSlot(1, 0) |
    240                                       ShiftButtonBitForNewSlot(2, 1);
    241      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
    242                                       ShiftButtonBitForNewSlot(1, 0, true) |
    243                                       ShiftButtonBitForNewSlot(2, 1, true);
    244      aControllerState.numButtons = 3;
    245      aControllerState.numAxes = 2;
    246 
    247      static Matrix4x4 focusPlusTransform;
    248      Matrix4x4 originalMtx;
    249      if (focusPlusTransform.IsIdentity()) {
    250        focusPlusTransform.RotateX(-0.70f);
    251        focusPlusTransform.PostTranslate(0.0f, 0.0f, 0.01f);
    252        focusPlusTransform.Inverse();
    253      }
    254      gfx::Quaternion quat(aControllerState.pose.orientation[0],
    255                           aControllerState.pose.orientation[1],
    256                           aControllerState.pose.orientation[2],
    257                           aControllerState.pose.orientation[3]);
    258      originalMtx.SetRotationFromQuaternion(quat);
    259      originalMtx._41 = aControllerState.pose.position[0];
    260      originalMtx._42 = aControllerState.pose.position[1];
    261      originalMtx._43 = aControllerState.pose.position[2];
    262      originalMtx = focusPlusTransform * originalMtx;
    263 
    264      gfx::Point3D pos, scale;
    265      originalMtx.Decompose(pos, quat, scale);
    266 
    267      aControllerState.pose.position[0] = pos.x;
    268      aControllerState.pose.position[1] = pos.y;
    269      aControllerState.pose.position[2] = pos.z;
    270 
    271      aControllerState.pose.orientation[0] = quat.x;
    272      aControllerState.pose.orientation[1] = quat.y;
    273      aControllerState.pose.orientation[2] = quat.z;
    274      aControllerState.pose.orientation[3] = quat.w;
    275      break;
    276    }
    277    case VRControllerType::OculusGo: {
    278      aControllerState.buttonPressed =
    279          ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
    280      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
    281                                       ShiftButtonBitForNewSlot(1, 0, true);
    282      aControllerState.numButtons = 2;
    283      aControllerState.numAxes = 2;
    284 
    285      static Matrix4x4 goTransform;
    286      Matrix4x4 originalMtx;
    287 
    288      if (goTransform.IsIdentity()) {
    289        goTransform.RotateX(-0.60f);
    290        goTransform.Inverse();
    291      }
    292      gfx::Quaternion quat(aControllerState.pose.orientation[0],
    293                           aControllerState.pose.orientation[1],
    294                           aControllerState.pose.orientation[2],
    295                           aControllerState.pose.orientation[3]);
    296      originalMtx.SetRotationFromQuaternion(quat);
    297      originalMtx._41 = aControllerState.pose.position[0];
    298      originalMtx._42 = aControllerState.pose.position[1];
    299      originalMtx._43 = aControllerState.pose.position[2];
    300      originalMtx = goTransform * originalMtx;
    301 
    302      gfx::Point3D pos, scale;
    303      originalMtx.Decompose(pos, quat, scale);
    304 
    305      aControllerState.pose.position[0] = pos.x;
    306      aControllerState.pose.position[1] = pos.y;
    307      aControllerState.pose.position[2] = pos.z;
    308 
    309      aControllerState.pose.orientation[0] = quat.x;
    310      aControllerState.pose.orientation[1] = quat.y;
    311      aControllerState.pose.orientation[2] = quat.z;
    312      aControllerState.pose.orientation[3] = quat.w;
    313      break;
    314    }
    315    case VRControllerType::OculusTouch:
    316      aControllerState.buttonPressed =
    317          ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
    318          ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
    319          ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
    320      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
    321                                       ShiftButtonBitForNewSlot(1, 0, true) |
    322                                       ShiftButtonBitForNewSlot(2, 1, true) |
    323                                       ShiftButtonBitForNewSlot(3, 4, true) |
    324                                       ShiftButtonBitForNewSlot(4, 5, true) |
    325                                       ShiftButtonBitForNewSlot(5, 6, true);
    326      aControllerState.axisValue[0] = aControllerState.axisValue[2];
    327      aControllerState.axisValue[1] = aControllerState.axisValue[3];
    328      aControllerState.numButtons = 6;
    329      aControllerState.numAxes = 2;
    330      break;
    331    case VRControllerType::OculusTouch2:
    332    case VRControllerType::OculusTouch3: {
    333      aControllerState.buttonPressed =
    334          ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
    335          ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
    336          ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
    337      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
    338                                       ShiftButtonBitForNewSlot(1, 0, true) |
    339                                       ShiftButtonBitForNewSlot(2, 1, true) |
    340                                       ShiftButtonBitForNewSlot(3, 4, true) |
    341                                       ShiftButtonBitForNewSlot(4, 5, true) |
    342                                       ShiftButtonBitForNewSlot(5, 6, true);
    343      aControllerState.axisValue[0] = aControllerState.axisValue[2];
    344      aControllerState.axisValue[1] = aControllerState.axisValue[3];
    345      aControllerState.numButtons = 6;
    346      aControllerState.numAxes = 2;
    347 
    348      static Matrix4x4 touchTransform;
    349      Matrix4x4 originalMtx;
    350 
    351      if (touchTransform.IsIdentity()) {
    352        touchTransform.RotateX(-0.77f);
    353        touchTransform.PostTranslate(0.0f, 0.0f, -0.025f);
    354        touchTransform.Inverse();
    355      }
    356      gfx::Quaternion quat(aControllerState.pose.orientation[0],
    357                           aControllerState.pose.orientation[1],
    358                           aControllerState.pose.orientation[2],
    359                           aControllerState.pose.orientation[3]);
    360      originalMtx.SetRotationFromQuaternion(quat);
    361      originalMtx._41 = aControllerState.pose.position[0];
    362      originalMtx._42 = aControllerState.pose.position[1];
    363      originalMtx._43 = aControllerState.pose.position[2];
    364      originalMtx = touchTransform * originalMtx;
    365 
    366      gfx::Point3D pos, scale;
    367      originalMtx.Decompose(pos, quat, scale);
    368 
    369      aControllerState.pose.position[0] = pos.x;
    370      aControllerState.pose.position[1] = pos.y;
    371      aControllerState.pose.position[2] = pos.z;
    372 
    373      aControllerState.pose.orientation[0] = quat.x;
    374      aControllerState.pose.orientation[1] = quat.y;
    375      aControllerState.pose.orientation[2] = quat.z;
    376      aControllerState.pose.orientation[3] = quat.w;
    377      break;
    378    }
    379    case VRControllerType::ValveIndex:
    380      aControllerState.buttonPressed =
    381          ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
    382          ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(5, 3) |
    383          ShiftButtonBitForNewSlot(3, 4) | ShiftButtonBitForNewSlot(4, 5) |
    384          ShiftButtonBitForNewSlot(6, 6) | ShiftButtonBitForNewSlot(7, 7) |
    385          ShiftButtonBitForNewSlot(8, 8) | ShiftButtonBitForNewSlot(9, 9);
    386      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(1, 0, true) |
    387                                       ShiftButtonBitForNewSlot(2, 1, true) |
    388                                       ShiftButtonBitForNewSlot(0, 2, true) |
    389                                       ShiftButtonBitForNewSlot(5, 3, true) |
    390                                       ShiftButtonBitForNewSlot(3, 4, true) |
    391                                       ShiftButtonBitForNewSlot(4, 5, true) |
    392                                       ShiftButtonBitForNewSlot(6, 6, true) |
    393                                       ShiftButtonBitForNewSlot(7, 7, true) |
    394                                       ShiftButtonBitForNewSlot(8, 8, true) |
    395                                       ShiftButtonBitForNewSlot(9, 9, true);
    396      aControllerState.numButtons = 10;
    397      aControllerState.numAxes = 4;
    398      break;
    399    case VRControllerType::PicoGaze:
    400      aControllerState.buttonPressed = ShiftButtonBitForNewSlot(0, 0);
    401      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 0, true);
    402      aControllerState.numButtons = 1;
    403      aControllerState.numAxes = 0;
    404      break;
    405    case VRControllerType::PicoG2:
    406      aControllerState.buttonPressed =
    407          ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
    408      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
    409                                       ShiftButtonBitForNewSlot(1, 0, true);
    410      aControllerState.numButtons = 2;
    411      aControllerState.numAxes = 2;
    412      break;
    413    case VRControllerType::PicoNeo2:
    414      aControllerState.buttonPressed =
    415          ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
    416          ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
    417          ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
    418      aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
    419                                       ShiftButtonBitForNewSlot(1, 0, true) |
    420                                       ShiftButtonBitForNewSlot(2, 1, true) |
    421                                       ShiftButtonBitForNewSlot(3, 4, true) |
    422                                       ShiftButtonBitForNewSlot(4, 5, true) |
    423                                       ShiftButtonBitForNewSlot(5, 6, true);
    424      aControllerState.axisValue[0] = aControllerState.axisValue[2];
    425      aControllerState.axisValue[1] = aControllerState.axisValue[3];
    426      aControllerState.numButtons = 6;
    427      aControllerState.numAxes = 2;
    428      break;
    429    default:
    430      // Undefined controller types, we will keep its the same order.
    431      break;
    432  }
    433 }
    434 
    435 void VRDisplayClient::FireGamepadEvents() {
    436  RefPtr<dom::GamepadManager> gamepadManager(dom::GamepadManager::GetService());
    437  if (!gamepadManager) {
    438    return;
    439  }
    440  for (int stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
    441    VRControllerState state = {}, lastState = {};
    442    memcpy(&state, &mDisplayInfo.mControllerState[stateIndex],
    443           sizeof(VRControllerState));
    444    memcpy(&lastState, &mLastEventControllerState[stateIndex],
    445           sizeof(VRControllerState));
    446    GamepadMappingForWebVR(state);
    447    GamepadMappingForWebVR(lastState);
    448 
    449    uint32_t gamepadHandleValue =
    450        mDisplayInfo.mDisplayID * kVRControllerMaxCount + stateIndex;
    451 
    452    GamepadHandle gamepadHandle{gamepadHandleValue, GamepadHandleKind::VR};
    453 
    454    bool bIsNew = false;
    455 
    456    // Send events to notify that controllers are removed
    457    if (state.controllerName[0] == '\0') {
    458      // Controller is not present
    459      if (lastState.controllerName[0] != '\0') {
    460        // Controller has been removed
    461        dom::GamepadRemoved info;
    462        dom::GamepadChangeEventBody body(info);
    463        dom::GamepadChangeEvent event(gamepadHandle, body);
    464        gamepadManager->Update(event);
    465      }
    466      // Do not process any further events for removed controllers
    467      continue;
    468    }
    469 
    470    // Send events to notify that new controllers are added
    471    RefPtr<dom::Gamepad> existing = gamepadManager->GetGamepad(gamepadHandle);
    472    // ControllerState in OpenVR action-based API gets delay to query btn and
    473    // axis count. So, we need to check if they are more than zero.
    474    if ((lastState.controllerName[0] == '\0' || !existing) &&
    475        (state.numButtons > 0 || state.numAxes > 0)) {
    476      dom::GamepadAdded info(NS_ConvertUTF8toUTF16(state.controllerName.data()),
    477                             dom::GamepadMappingType::_empty, state.hand,
    478                             state.numButtons, state.numAxes, state.numHaptics,
    479                             0, 0);
    480      dom::GamepadChangeEventBody body(info);
    481      dom::GamepadChangeEvent event(gamepadHandle, body);
    482      gamepadManager->Update(event);
    483      bIsNew = true;
    484    }
    485 
    486    // Send events for handedness changes
    487    if (state.hand != lastState.hand) {
    488      dom::GamepadHandInformation info(state.hand);
    489      dom::GamepadChangeEventBody body(info);
    490      dom::GamepadChangeEvent event(gamepadHandle, body);
    491      gamepadManager->Update(event);
    492    }
    493 
    494    // Send events for axis value changes
    495    for (uint32_t axisIndex = 0; axisIndex < state.numAxes; axisIndex++) {
    496      if (state.axisValue[axisIndex] != lastState.axisValue[axisIndex]) {
    497        dom::GamepadAxisInformation info(axisIndex, state.axisValue[axisIndex]);
    498        dom::GamepadChangeEventBody body(info);
    499        dom::GamepadChangeEvent event(gamepadHandle, body);
    500        gamepadManager->Update(event);
    501      }
    502    }
    503 
    504    // Send events for trigger, touch, and button value changes
    505    if (!bIsNew) {
    506      // When a new controller is added, we do not emit button events for
    507      // the initial state of the inputs.
    508      for (uint32_t buttonIndex = 0; buttonIndex < state.numButtons;
    509           buttonIndex++) {
    510        bool bPressed = (state.buttonPressed & (1ULL << buttonIndex)) != 0;
    511        bool bTouched = (state.buttonTouched & (1ULL << buttonIndex)) != 0;
    512        bool bLastPressed =
    513            (lastState.buttonPressed & (1ULL << buttonIndex)) != 0;
    514        bool bLastTouched =
    515            (lastState.buttonTouched & (1ULL << buttonIndex)) != 0;
    516 
    517        if (state.triggerValue[buttonIndex] !=
    518                lastState.triggerValue[buttonIndex] ||
    519            bPressed != bLastPressed || bTouched != bLastTouched) {
    520          dom::GamepadButtonInformation info(
    521              buttonIndex, state.triggerValue[buttonIndex], bPressed, bTouched);
    522          dom::GamepadChangeEventBody body(info);
    523          dom::GamepadChangeEvent event(gamepadHandle, body);
    524          gamepadManager->Update(event);
    525        }
    526      }
    527    }
    528 
    529    // Send events for pose changes
    530    // Note that VRPose is asserted to be a POD type so memcmp is safe
    531    if (state.flags != lastState.flags ||
    532        state.isPositionValid != lastState.isPositionValid ||
    533        state.isOrientationValid != lastState.isOrientationValid ||
    534        memcmp(&state.pose, &lastState.pose, sizeof(VRPose)) != 0) {
    535      // Convert pose to GamepadPoseState
    536      dom::GamepadPoseState poseState;
    537      poseState.Clear();
    538      poseState.flags = state.flags;
    539 
    540      // Orientation values
    541      poseState.isOrientationValid = state.isOrientationValid;
    542      poseState.orientation[0] = state.pose.orientation[0];
    543      poseState.orientation[1] = state.pose.orientation[1];
    544      poseState.orientation[2] = state.pose.orientation[2];
    545      poseState.orientation[3] = state.pose.orientation[3];
    546      poseState.angularVelocity[0] = state.pose.angularVelocity[0];
    547      poseState.angularVelocity[1] = state.pose.angularVelocity[1];
    548      poseState.angularVelocity[2] = state.pose.angularVelocity[2];
    549      poseState.angularAcceleration[0] = state.pose.angularAcceleration[0];
    550      poseState.angularAcceleration[1] = state.pose.angularAcceleration[1];
    551      poseState.angularAcceleration[2] = state.pose.angularAcceleration[2];
    552 
    553      // Position values
    554      poseState.isPositionValid = state.isPositionValid;
    555      poseState.position[0] = state.pose.position[0];
    556      poseState.position[1] = state.pose.position[1];
    557      poseState.position[2] = state.pose.position[2];
    558      poseState.linearVelocity[0] = state.pose.linearVelocity[0];
    559      poseState.linearVelocity[1] = state.pose.linearVelocity[1];
    560      poseState.linearVelocity[2] = state.pose.linearVelocity[2];
    561      poseState.linearAcceleration[0] = state.pose.linearAcceleration[0];
    562      poseState.linearAcceleration[1] = state.pose.linearAcceleration[1];
    563      poseState.linearAcceleration[2] = state.pose.linearAcceleration[2];
    564 
    565      // Send the event
    566      dom::GamepadPoseInformation info(poseState);
    567      dom::GamepadChangeEventBody body(info);
    568      dom::GamepadChangeEvent event(gamepadHandle, body);
    569      gamepadManager->Update(event);
    570    }
    571  }
    572 
    573  mLastEventControllerState = mDisplayInfo.mControllerState;
    574 }
    575 
    576 const VRHMDSensorState& VRDisplayClient::GetSensorState() const {
    577  return mDisplayInfo.GetSensorState();
    578 }
    579 
    580 bool VRDisplayClient::GetIsConnected() const {
    581  return mDisplayInfo.GetIsConnected();
    582 }
    583 
    584 bool VRDisplayClient::IsPresenting() {
    585  return mDisplayInfo.mPresentingGroups != 0;
    586 }
    587 
    588 void VRDisplayClient::NotifyDisconnected() {
    589  mDisplayInfo.mDisplayState.isConnected = false;
    590 }
    591 
    592 void VRDisplayClient::UpdateSubmitFrameResult(
    593    const VRSubmitFrameResultInfo& aResult) {
    594  mSubmitFrameResult = aResult;
    595 }
    596 
    597 void VRDisplayClient::GetSubmitFrameResult(VRSubmitFrameResultInfo& aResult) {
    598  aResult = mSubmitFrameResult;
    599 }
    600 
    601 void VRDisplayClient::StartVRNavigation() {
    602  /**
    603   * A VR-to-VR site navigation has started, notify VRManager
    604   * so we don't drop out of VR during the transition
    605   */
    606  VRManagerChild* vm = VRManagerChild::Get();
    607  vm->SendStartVRNavigation(mDisplayInfo.mDisplayID);
    608 }
    609 
    610 void VRDisplayClient::StopVRNavigation(const TimeDuration& aTimeout) {
    611  /**
    612   * A VR-to-VR site navigation has ended and the new site
    613   * has received a vrdisplayactivate event.
    614   * Don't actually consider the navigation transition over
    615   * until aTimeout has elapsed.
    616   * This may be called multiple times, in which case the timeout
    617   * should be reset to aTimeout.
    618   * When aTimeout is TimeDuration(0), we should consider the
    619   * transition immediately ended.
    620   */
    621  VRManagerChild* vm = VRManagerChild::Get();
    622  vm->SendStopVRNavigation(mDisplayInfo.mDisplayID, aTimeout);
    623 }
    624 
    625 bool VRDisplayClient::IsReferenceSpaceTypeSupported(
    626    dom::XRReferenceSpaceType aType) const {
    627  /**
    628   * https://immersive-web.github.io/webxr/#reference-space-is-supported
    629   *
    630   * We do not yet support local or local-floor for inline sessions.
    631   * This could be expanded if we later support WebXR for inline-ar
    632   * sessions on Firefox Fenix.
    633   *
    634   * We do not yet support unbounded reference spaces.
    635   */
    636  switch (aType) {
    637    case dom::XRReferenceSpaceType::Viewer:
    638      // Viewer is always supported, for both inline and immersive sessions
    639      return true;
    640    case dom::XRReferenceSpaceType::Local:
    641    case dom::XRReferenceSpaceType::Local_floor:
    642      // Local and Local_Floor are always supported for immersive sessions
    643      return bool(mDisplayInfo.GetCapabilities() &
    644                  (VRDisplayCapabilityFlags::Cap_ImmersiveVR |
    645                   VRDisplayCapabilityFlags::Cap_ImmersiveAR));
    646    case dom::XRReferenceSpaceType::Bounded_floor:
    647      return bool(mDisplayInfo.GetCapabilities() &
    648                  VRDisplayCapabilityFlags::Cap_StageParameters);
    649    default:
    650      NS_WARNING(
    651          "Unknown XRReferenceSpaceType passed to "
    652          "VRDisplayClient::IsReferenceSpaceTypeSupported");
    653      return false;
    654  }
    655 }