Bug 1470527 - Implement Controller support for gfxVRExternal and VRServiceOpenVR
☠☠ backed out by ad44ecfdc7b4 ☠ ☠
authorKearwood Gilbert <kgilbert@mozilla.com>
Wed, 11 Jul 2018 12:20:51 -0700
changeset 426255 66c97a5d22efc6436aab76756759b9b51517e940
parent 426254 10ce7c1ca2d37cc0d26c70616c9c553e504fbe03
child 426256 ad44ecfdc7b447290d45715f67ad70fd0a44aa91
push id34269
push userebalazs@mozilla.com
push dateThu, 12 Jul 2018 09:28:30 +0000
treeherdermozilla-central@fe17acc6e291 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1470527
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1470527 - Implement Controller support for gfxVRExternal and VRServiceOpenVR
dom/vr/VRDisplay.cpp
dom/vr/VRServiceTest.cpp
gfx/vr/VRDisplayClient.cpp
gfx/vr/VRDisplayClient.h
gfx/vr/VRDisplayHost.cpp
gfx/vr/external_api/moz_external_vr.h
gfx/vr/gfxVR.cpp
gfx/vr/gfxVR.h
gfx/vr/gfxVRExternal.cpp
gfx/vr/gfxVRExternal.h
gfx/vr/gfxVROSVR.cpp
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVRPuppet.cpp
gfx/vr/ipc/VRManagerChild.cpp
gfx/vr/ipc/VRManagerChild.h
gfx/vr/ipc/VRMessageUtils.h
gfx/vr/service/OpenVRSession.cpp
gfx/vr/service/OpenVRSession.h
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -280,68 +280,68 @@ VRPose::~VRPose()
   mozilla::DropJSObjects(this);
 }
 
 void
 VRPose::GetPosition(JSContext* aCx,
                     JS::MutableHandle<JSObject*> aRetval,
                     ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mPosition, mVRState.position, 3,
+  SetFloat32Array(aCx, aRetval, mPosition, mVRState.pose.position, 3,
     !mPosition && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position),
     aRv);
 }
 
 void
 VRPose::GetLinearVelocity(JSContext* aCx,
                           JS::MutableHandle<JSObject*> aRetval,
                           ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mLinearVelocity, mVRState.linearVelocity, 3,
+  SetFloat32Array(aCx, aRetval, mLinearVelocity, mVRState.pose.linearVelocity, 3,
     !mLinearVelocity && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position),
     aRv);
 }
 
 void
 VRPose::GetLinearAcceleration(JSContext* aCx,
                               JS::MutableHandle<JSObject*> aRetval,
                               ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mLinearAcceleration, mVRState.linearAcceleration, 3,
+  SetFloat32Array(aCx, aRetval, mLinearAcceleration, mVRState.pose.linearAcceleration, 3,
     !mLinearAcceleration && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration),
     aRv);
 
 }
 
 void
 VRPose::GetOrientation(JSContext* aCx,
                        JS::MutableHandle<JSObject*> aRetval,
                        ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mOrientation, mVRState.orientation, 4,
+  SetFloat32Array(aCx, aRetval, mOrientation, mVRState.pose.orientation, 4,
     !mOrientation && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation),
     aRv);
 }
 
 void
 VRPose::GetAngularVelocity(JSContext* aCx,
                            JS::MutableHandle<JSObject*> aRetval,
                            ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mAngularVelocity, mVRState.angularVelocity, 3,
+  SetFloat32Array(aCx, aRetval, mAngularVelocity, mVRState.pose.angularVelocity, 3,
     !mAngularVelocity && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation),
     aRv);
 }
 
 void
 VRPose::GetAngularAcceleration(JSContext* aCx,
                                JS::MutableHandle<JSObject*> aRetval,
                                ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mAngularAcceleration, mVRState.angularAcceleration, 3,
+  SetFloat32Array(aCx, aRetval, mAngularAcceleration, mVRState.pose.angularAcceleration, 3,
     !mAngularAcceleration && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration),
     aRv);
 }
 
 JSObject*
 VRPose::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VRPose_Binding::Wrap(aCx, this, aGivenProto);
--- a/dom/vr/VRServiceTest.cpp
+++ b/dom/vr/VRServiceTest.cpp
@@ -90,60 +90,60 @@ VRMockDisplay::SetPose(const Nullable<Fl
                        VRDisplayCapabilityFlags::Cap_External |
                        VRDisplayCapabilityFlags::Cap_MountDetection |
                        VRDisplayCapabilityFlags::Cap_Present;
 
   if (!aOrientation.IsNull()) {
     const Float32Array& value = aOrientation.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 4);
-    mSensorState.orientation[0] = value.Data()[0];
-    mSensorState.orientation[1] = value.Data()[1];
-    mSensorState.orientation[2] = value.Data()[2];
-    mSensorState.orientation[3] = value.Data()[3];
+    mSensorState.pose.orientation[0] = value.Data()[0];
+    mSensorState.pose.orientation[1] = value.Data()[1];
+    mSensorState.pose.orientation[2] = value.Data()[2];
+    mSensorState.pose.orientation[3] = value.Data()[3];
   }
   if (!aAngularVelocity.IsNull()) {
     const Float32Array& value = aAngularVelocity.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.angularVelocity[0] = value.Data()[0];
-    mSensorState.angularVelocity[1] = value.Data()[1];
-    mSensorState.angularVelocity[2] = value.Data()[2];
+    mSensorState.pose.angularVelocity[0] = value.Data()[0];
+    mSensorState.pose.angularVelocity[1] = value.Data()[1];
+    mSensorState.pose.angularVelocity[2] = value.Data()[2];
   }
   if (!aAngularAcceleration.IsNull()) {
     const Float32Array& value = aAngularAcceleration.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.angularAcceleration[0] = value.Data()[0];
-    mSensorState.angularAcceleration[1] = value.Data()[1];
-    mSensorState.angularAcceleration[2] = value.Data()[2];
+    mSensorState.pose.angularAcceleration[0] = value.Data()[0];
+    mSensorState.pose.angularAcceleration[1] = value.Data()[1];
+    mSensorState.pose.angularAcceleration[2] = value.Data()[2];
   }
   if (!aPosition.IsNull()) {
     const Float32Array& value = aPosition.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.position[0] = value.Data()[0];
-    mSensorState.position[1] = value.Data()[1];
-    mSensorState.position[2] = value.Data()[2];
+    mSensorState.pose.position[0] = value.Data()[0];
+    mSensorState.pose.position[1] = value.Data()[1];
+    mSensorState.pose.position[2] = value.Data()[2];
   }
   if (!aLinearVelocity.IsNull()) {
     const Float32Array& value = aLinearVelocity.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.linearVelocity[0] = value.Data()[0];
-    mSensorState.linearVelocity[1] = value.Data()[1];
-    mSensorState.linearVelocity[2] = value.Data()[2];
+    mSensorState.pose.linearVelocity[0] = value.Data()[0];
+    mSensorState.pose.linearVelocity[1] = value.Data()[1];
+    mSensorState.pose.linearVelocity[2] = value.Data()[2];
   }
   if (!aLinearAcceleration.IsNull()) {
     const Float32Array& value = aLinearAcceleration.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.linearAcceleration[0] = value.Data()[0];
-    mSensorState.linearAcceleration[1] = value.Data()[1];
-    mSensorState.linearAcceleration[2] = value.Data()[2];
+    mSensorState.pose.linearAcceleration[0] = value.Data()[0];
+    mSensorState.pose.linearAcceleration[1] = value.Data()[1];
+    mSensorState.pose.linearAcceleration[2] = value.Data()[2];
   }
 }
 
 void
 VRMockDisplay::Update()
 {
   gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
 
--- a/gfx/vr/VRDisplayClient.cpp
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -4,17 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <math.h>
 
 #include "prlink.h"
 #include "prenv.h"
 #include "gfxPrefs.h"
+#include "nsIGlobalObject.h"
+#include "nsRefPtrHashtable.h"
 #include "nsString.h"
+#include "mozilla/dom/GamepadManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIScreenManager.h"
 
 #ifdef XP_WIN
 #include "../layers/d3d11/CompositorD3D11.h"
 #endif
@@ -29,16 +32,17 @@ using namespace mozilla::gfx;
 
 VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
   : mDisplayInfo(aDisplayInfo)
   , bLastEventWasMounted(false)
   , bLastEventWasPresenting(false)
   , mPresentationCount(0)
   , mLastEventFrameId(0)
   , mLastPresentingGeneration(0)
+  , mLastEventControllerState{}
 {
   MOZ_COUNT_CTOR(VRDisplayClient);
 }
 
 VRDisplayClient::~VRDisplayClient() {
   MOZ_COUNT_DTOR(VRDisplayClient);
 }
 
@@ -123,16 +127,155 @@ VRDisplayClient::FireEvents()
     }
   }
 
   // Check if we need to trigger VRDisplay.requestAnimationFrame
   if (mLastEventFrameId != mDisplayInfo.mFrameId) {
     mLastEventFrameId = mDisplayInfo.mFrameId;
     vm->RunFrameRequestCallbacks();
   }
+
+  FireGamepadEvents();
+}
+
+void
+VRDisplayClient::FireGamepadEvents()
+{
+  // VRManagerChild could be at other processes, but GamepadManager
+  // only exists at the content process or the same process
+  // in non-e10s mode.
+  MOZ_ASSERT(XRE_IsContentProcess() || IsSameProcess());
+
+  RefPtr<dom::GamepadManager> gamepadManager(dom::GamepadManager::GetService());
+  if (!gamepadManager) {
+    return;
+  }
+  for (int stateIndex=0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    const VRControllerState& state = mDisplayInfo.mControllerState[stateIndex];
+    const VRControllerState& lastState = mLastEventControllerState[stateIndex];
+    uint32_t gamepadId = mDisplayInfo.mDisplayID * kVRControllerMaxCount + stateIndex;
+    bool bIsNew = false;
+
+    // Send events to notify that controllers are removed
+    if (state.controllerName[0] == '\0') {
+      // Controller is not present
+      if (lastState.controllerName[0] != '\0') {
+        // Controller has been removed
+        dom::GamepadRemoved info;
+        dom::GamepadChangeEventBody body(info);
+        dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+        gamepadManager->Update(event);
+      }
+      // Do not process any further events for removed controllers
+      continue;
+    }
+
+    // Send events to notify that new controllers are added
+    if (lastState.controllerName[0] == '\0') {
+      dom::GamepadAdded info(NS_ConvertUTF8toUTF16(state.controllerName),
+                             dom::GamepadMappingType::_empty,
+                             state.hand,
+                             mDisplayInfo.mDisplayID,
+                             state.numButtons,
+                             state.numAxes,
+                             state.numHaptics);
+      dom::GamepadChangeEventBody body(info);
+      dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+      gamepadManager->Update(event);
+      bIsNew = true;
+    }
+
+    // Send events for handedness changes
+    if (state.hand != lastState.hand) {
+      dom::GamepadHandInformation info(state.hand);
+      dom::GamepadChangeEventBody body(info);
+      dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+      gamepadManager->Update(event);
+    }
+
+    // Send events for axis value changes
+    for (uint32_t axisIndex = 0; axisIndex < state.numAxes; axisIndex++) {
+      if (state.axisValue[axisIndex] != lastState.axisValue[axisIndex]) {
+        dom::GamepadAxisInformation info(axisIndex, state.axisValue[axisIndex]);
+        dom::GamepadChangeEventBody body(info);
+        dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+        gamepadManager->Update(event);
+      }
+    }
+
+    // Send events for trigger, touch, and button value changes
+    if (!bIsNew) {
+      // When a new controller is added, we do not emit button events for
+      // the initial state of the inputs.
+      for (uint32_t buttonIndex = 0; buttonIndex < state.numButtons; buttonIndex++) {
+        bool bPressed = (state.buttonPressed & (1ULL << buttonIndex)) != 0;
+        bool bTouched = (state.buttonTouched & (1ULL << buttonIndex)) != 0;
+        bool bLastPressed = (lastState.buttonPressed & (1ULL << buttonIndex)) != 0;
+        bool bLastTouched = (lastState.buttonTouched & (1ULL << buttonIndex)) != 0;
+
+        if (state.triggerValue[buttonIndex] != lastState.triggerValue[buttonIndex] ||
+            bPressed != bLastPressed ||
+            bTouched != bLastTouched) {
+          dom::GamepadButtonInformation info(buttonIndex, state.triggerValue[buttonIndex], bPressed, bTouched);
+          dom::GamepadChangeEventBody body(info);
+          dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+          gamepadManager->Update(event);
+        }
+      }
+    }
+
+    // Send events for pose changes
+    // Note that VRPose is asserted to be a POD type so memcmp is safe
+    if (state.flags != lastState.flags ||
+        state.isPositionValid != lastState.isPositionValid ||
+        state.isOrientationValid != lastState.isOrientationValid ||
+        memcmp(&state.pose, &lastState.pose, sizeof(VRPose)) != 0) {
+
+      // Convert pose to GamepadPoseState
+      dom::GamepadPoseState poseState;
+      poseState.Clear();
+      poseState.flags = state.flags;
+
+      // Orientation values
+      poseState.isOrientationValid = state.isOrientationValid;
+      poseState.orientation[0] = state.pose.orientation[0];
+      poseState.orientation[1] = state.pose.orientation[1];
+      poseState.orientation[2] = state.pose.orientation[2];
+      poseState.orientation[3] = state.pose.orientation[3];
+      poseState.angularVelocity[0] = state.pose.angularVelocity[0];
+      poseState.angularVelocity[1] = state.pose.angularVelocity[1];
+      poseState.angularVelocity[2] = state.pose.angularVelocity[2];
+      poseState.angularAcceleration[0] = state.pose.angularAcceleration[0];
+      poseState.angularAcceleration[1] = state.pose.angularAcceleration[1];
+      poseState.angularAcceleration[2] = state.pose.angularAcceleration[2];
+
+      // Position values
+      poseState.isPositionValid = state.isPositionValid;
+      poseState.position[0] = state.pose.position[0];
+      poseState.position[1] = state.pose.position[1];
+      poseState.position[2] = state.pose.position[2];
+      poseState.linearVelocity[0] = state.pose.linearVelocity[0];
+      poseState.linearVelocity[1] = state.pose.linearVelocity[1];
+      poseState.linearVelocity[2] = state.pose.linearVelocity[2];
+      poseState.linearAcceleration[0] = state.pose.linearAcceleration[0];
+      poseState.linearAcceleration[1] = state.pose.linearAcceleration[1];
+      poseState.linearAcceleration[2] = state.pose.linearAcceleration[2];
+
+      // Send the event
+      dom::GamepadPoseInformation info(poseState);
+      dom::GamepadChangeEventBody body(info);
+      dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+      gamepadManager->Update(event);
+    }
+  }
+
+  // Note that VRControllerState is asserted to be a POD type and memcpy is safe.
+  memcpy(mLastEventControllerState,
+         mDisplayInfo.mControllerState,
+         sizeof(VRControllerState) * kVRControllerMaxCount);
 }
 
 VRHMDSensorState
 VRDisplayClient::GetSensorState()
 {
   return mDisplayInfo.GetSensorState();
 }
 
--- a/gfx/vr/VRDisplayClient.h
+++ b/gfx/vr/VRDisplayClient.h
@@ -46,25 +46,30 @@ public:
 
   bool IsPresentationGenerationCurrent() const;
   void MakePresentationGenerationCurrent();
 
 protected:
   virtual ~VRDisplayClient();
 
   void FireEvents();
+  void FireGamepadEvents();
 
   VRDisplayInfo mDisplayInfo;
 
   bool bLastEventWasMounted;
   bool bLastEventWasPresenting;
 
   int mPresentationCount;
   uint64_t mLastEventFrameId;
   uint32_t mLastPresentingGeneration;
+
+  // Difference between mDisplayInfo.mControllerState and mLastEventControllerState
+  // determines what gamepad events to fire when updated.
+  VRControllerState mLastEventControllerState[kVRControllerMaxCount];
 private:
   VRSubmitFrameResultInfo mSubmitFrameResult;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_DISPLAY_CLIENT_H */
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -347,17 +347,17 @@ VRDisplayHost::CheckClearDisplayInfoDirt
 
 VRControllerHost::VRControllerHost(VRDeviceType aType, dom::GamepadHand aHand,
                                    uint32_t aDisplayID)
  : mControllerInfo{}
  , mVibrateIndex(0)
 {
   MOZ_COUNT_CTOR(VRControllerHost);
   mControllerInfo.mType = aType;
-  mControllerInfo.mControllerState.mHand = aHand;
+  mControllerInfo.mControllerState.hand = aHand;
   mControllerInfo.mMappingType = dom::GamepadMappingType::_empty;
   mControllerInfo.mDisplayID = aDisplayID;
   mControllerInfo.mControllerID = VRSystemManager::AllocateControllerID();
 }
 
 VRControllerHost::~VRControllerHost()
 {
   MOZ_COUNT_DTOR(VRControllerHost);
@@ -367,35 +367,35 @@ const VRControllerInfo&
 VRControllerHost::GetControllerInfo() const
 {
   return mControllerInfo;
 }
 
 void
 VRControllerHost::SetButtonPressed(uint64_t aBit)
 {
-  mControllerInfo.mControllerState.mButtonPressed = aBit;
+  mControllerInfo.mControllerState.buttonPressed = aBit;
 }
 
 uint64_t
 VRControllerHost::GetButtonPressed()
 {
-  return mControllerInfo.mControllerState.mButtonPressed;
+  return mControllerInfo.mControllerState.buttonPressed;
 }
 
 void
 VRControllerHost::SetButtonTouched(uint64_t aBit)
 {
-  mControllerInfo.mControllerState.mButtonTouched = aBit;
+  mControllerInfo.mControllerState.buttonTouched = aBit;
 }
 
 uint64_t
 VRControllerHost::GetButtonTouched()
 {
-  return mControllerInfo.mControllerState.mButtonTouched;
+  return mControllerInfo.mControllerState.buttonTouched;
 }
 
 void
 VRControllerHost::SetPose(const dom::GamepadPoseState& aPose)
 {
   mPose = aPose;
 }
 
@@ -403,17 +403,17 @@ const dom::GamepadPoseState&
 VRControllerHost::GetPose()
 {
   return mPose;
 }
 
 dom::GamepadHand
 VRControllerHost::GetHand()
 {
-  return mControllerInfo.mControllerState.mHand;
+  return mControllerInfo.mControllerState.hand;
 }
 
 void
 VRControllerHost::SetVibrateIndex(uint64_t aIndex)
 {
   mVibrateIndex = aIndex;
 }
 
--- a/gfx/vr/external_api/moz_external_vr.h
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -19,37 +19,38 @@
 #if defined(__ANDROID__)
 #include <pthread.h>
 #endif // defined(__ANDROID__)
 
 namespace mozilla {
 #ifdef MOZILLA_INTERNAL_API
 namespace dom {
   enum class GamepadHand : uint8_t;
+  enum class GamepadCapabilityFlags : uint16_t;
 }
 #endif //  MOZILLA_INTERNAL_API
 namespace gfx {
 
-static const int32_t kVRExternalVersion = 0;
+static const int32_t kVRExternalVersion = 1;
 
 // We assign VR presentations to groups with a bitmask.
 // Currently, we will only display either content or chrome.
 // Later, we will have more groups to support VR home spaces and
 // multitasking environments.
 // These values are not exposed to regular content and only affect
 // chrome-only API's.  They may be changed at any time.
 static const uint32_t kVRGroupNone = 0;
 static const uint32_t kVRGroupContent = 1 << 0;
 static const uint32_t kVRGroupChrome = 1 << 1;
 static const uint32_t kVRGroupAll = 0xffffffff;
 
 static const int kVRDisplayNameMaxLen = 256;
 static const int kVRControllerNameMaxLen = 256;
 static const int kVRControllerMaxCount = 16;
-static const int kVRControllerMaxTriggers = 16;
+static const int kVRControllerMaxButtons = 64;
 static const int kVRControllerMaxAxis = 16;
 static const int kVRLayerMaxCount = 8;
 
 #if defined(__ANDROID__)
 typedef uint64_t VRLayerTextureHandle;
 #else
 typedef void* VRLayerTextureHandle;
 #endif
@@ -77,16 +78,42 @@ struct FloatSize_POD
 
 enum class ControllerHand : uint8_t {
   _empty,
   Left,
   Right,
   EndGuard_
 };
 
+enum class ControllerCapabilityFlags : uint16_t {
+  Cap_None = 0,
+  /**
+   * Cap_Position is set if the Gamepad is capable of tracking its position.
+   */
+  Cap_Position = 1 << 1,
+  /**
+    * Cap_Orientation is set if the Gamepad is capable of tracking its orientation.
+    */
+  Cap_Orientation = 1 << 2,
+  /**
+   * Cap_AngularAcceleration is set if the Gamepad is capable of tracking its
+   * angular acceleration.
+   */
+  Cap_AngularAcceleration = 1 << 3,
+  /**
+   * Cap_LinearAcceleration is set if the Gamepad is capable of tracking its
+   * linear acceleration.
+   */
+  Cap_LinearAcceleration = 1 << 4,
+  /**
+   * Cap_All used for validity checking during IPC serialization
+   */
+  Cap_All = (1 << 5) - 1
+};
+
 #endif // ifndef MOZILLA_INTERNAL_API
 
 enum class VRDisplayCapabilityFlags : uint16_t {
   Cap_None = 0,
   /**
    * Cap_Position is set if the VRDisplay is capable of tracking its position.
    */
   Cap_Position = 1 << 1,
@@ -135,30 +162,35 @@ enum class VRDisplayCapabilityFlags : ui
    */
   Cap_All = (1 << 9) - 1
 };
 
 #ifdef MOZILLA_INTERNAL_API
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
 #endif // MOZILLA_INTERNAL_API
 
+struct VRPose
+{
+  float orientation[4];
+  float position[3];
+  float angularVelocity[3];
+  float angularAcceleration[3];
+  float linearVelocity[3];
+  float linearAcceleration[3];
+};
+
 struct VRHMDSensorState {
   uint64_t inputFrameID;
   double timestamp;
   VRDisplayCapabilityFlags flags;
 
   // These members will only change with inputFrameID:
-  float orientation[4];
-  float position[3];
+  VRPose pose;
   float leftViewMatrix[16];
   float rightViewMatrix[16];
-  float angularVelocity[3];
-  float angularAcceleration[3];
-  float linearVelocity[3];
-  float linearAcceleration[3];
 
 #ifdef MOZILLA_INTERNAL_API
 
   void Clear() {
     memset(this, 0, sizeof(VRHMDSensorState));
   }
 
   bool operator==(const VRHMDSensorState& other) const {
@@ -243,32 +275,40 @@ struct VRDisplayState
   float mSittingToStandingTransform[16];
   uint64_t mLastSubmittedFrameId;
   bool mLastSubmittedFrameSuccessful;
   uint32_t mPresentingGeneration;
 };
 
 struct VRControllerState
 {
-  char mControllerName[kVRControllerNameMaxLen];
+  char controllerName[kVRControllerNameMaxLen];
 #ifdef MOZILLA_INTERNAL_API
-  dom::GamepadHand mHand;
+  dom::GamepadHand hand;
 #else
-  ControllerHand mHand;
+  ControllerHand hand;
 #endif
-  uint32_t mNumButtons;
-  uint32_t mNumAxes;
-  uint32_t mNumTriggers;
-  uint32_t mNumHaptics;
+  uint32_t numButtons;
+  uint32_t numAxes;
+  uint32_t numHaptics;
   // The current button pressed bit of button mask.
-  uint64_t mButtonPressed;
+  uint64_t buttonPressed;
   // The current button touched bit of button mask.
-  uint64_t mButtonTouched;
-  float mTriggerValue[kVRControllerMaxTriggers];
-  float mAxisValue[kVRControllerMaxAxis];
+  uint64_t buttonTouched;
+  float triggerValue[kVRControllerMaxButtons];
+  float axisValue[kVRControllerMaxAxis];
+
+#ifdef MOZILLA_INTERNAL_API
+  dom::GamepadCapabilityFlags flags;
+#else
+  ControllerCapabilityFlags flags;
+#endif
+  VRPose pose;
+  bool isPositionValid;
+  bool isOrientationValid;
 };
 
 struct VRLayerEyeRect
 {
   float x;
   float y;
   float width;
   float height;
--- a/gfx/vr/gfxVR.cpp
+++ b/gfx/vr/gfxVR.cpp
@@ -184,20 +184,20 @@ VRSystemManager::NewHandChangeEvent(uint
 }
 
 void
 VRHMDSensorState::CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms)
 {
 
   gfx::Matrix4x4 matHead;
   if (flags & VRDisplayCapabilityFlags::Cap_Orientation) {
-    matHead.SetRotationFromQuaternion(gfx::Quaternion(orientation[0], orientation[1],
-                                                      orientation[2], orientation[3]));
+    matHead.SetRotationFromQuaternion(gfx::Quaternion(pose.orientation[0], pose.orientation[1],
+                                                      pose.orientation[2], pose.orientation[3]));
   }
-  matHead.PreTranslate(-position[0], -position[1], -position[2]);
+  matHead.PreTranslate(-pose.position[0], -pose.position[1], -pose.position[2]);
 
   gfx::Matrix4x4 matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Left];
   matView.Normalize();
   memcpy(leftViewMatrix, matView.components, sizeof(matView.components));
   matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Right];
   matView.Normalize();
   memcpy(rightViewMatrix, matView.components, sizeof(matView.components));
 }
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -55,16 +55,17 @@ enum class VRDeviceType : uint16_t {
 struct VRDisplayInfo
 {
   uint32_t mDisplayID;
   VRDeviceType mType;
   uint32_t mPresentingGroups;
   uint32_t mGroupMask;
   uint64_t mFrameId;
   VRDisplayState mDisplayState;
+  VRControllerState mControllerState[kVRControllerMaxCount];
 
   VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
   const VRHMDSensorState& GetSensorState() const
   {
     return mLastSensorState[mFrameId % kVRMaxLatencyFrames];
   }
 
   VRDeviceType GetType() const { return mType; }
@@ -84,20 +85,21 @@ struct VRDisplayInfo
   uint64_t GetFrameId() const { return mFrameId; }
 
   bool operator==(const VRDisplayInfo& other) const {
     for (size_t i = 0; i < kVRMaxLatencyFrames; i++) {
       if (mLastSensorState[i] != other.mLastSensorState[i]) {
         return false;
       }
     }
-    // Note that mDisplayState is asserted to be a POD type, so memcmp is safe
+    // Note that mDisplayState and mControllerState are asserted to be POD types, so memcmp is safe
     return mType == other.mType &&
            mDisplayID == other.mDisplayID &&
            memcmp(&mDisplayState, &other.mDisplayState, sizeof(VRDisplayState)) == 0 &&
+           memcmp(mControllerState, other.mControllerState, sizeof(VRControllerState) * kVRControllerMaxCount) == 0 &&
            mPresentingGroups == other.mPresentingGroups &&
            mGroupMask == other.mGroupMask &&
            mFrameId == other.mFrameId;
   }
 
   bool operator!=(const VRDisplayInfo& other) const {
     return !(*this == other);
   }
@@ -118,39 +120,36 @@ struct VRSubmitFrameResultInfo
   uint32_t mWidth;
   uint32_t mHeight;
 };
 
 struct VRControllerInfo
 {
   VRDeviceType GetType() const { return mType; }
   uint32_t GetControllerID() const { return mControllerID; }
-  const char* GetControllerName() const { return mControllerState.mControllerName; }
+  const char* GetControllerName() const { return mControllerState.controllerName; }
   dom::GamepadMappingType GetMappingType() const { return mMappingType; }
   uint32_t GetDisplayID() const { return mDisplayID; }
-  dom::GamepadHand GetHand() const { return mControllerState.mHand; }
-  uint32_t GetNumButtons() const { return mControllerState.mNumButtons; }
-  uint32_t GetNumAxes() const { return mControllerState.mNumAxes; }
-  uint32_t GetNumHaptics() const { return mControllerState.mNumHaptics; }
+  dom::GamepadHand GetHand() const { return mControllerState.hand; }
+  uint32_t GetNumButtons() const { return mControllerState.numButtons; }
+  uint32_t GetNumAxes() const { return mControllerState.numAxes; }
+  uint32_t GetNumHaptics() const { return mControllerState.numHaptics; }
 
   uint32_t mControllerID;
   VRDeviceType mType;
   dom::GamepadMappingType mMappingType;
   uint32_t mDisplayID;
   VRControllerState mControllerState;
   bool operator==(const VRControllerInfo& other) const {
+    // Note that mControllerState is asserted to be a POD type, so memcmp is safe
     return mType == other.mType &&
            mControllerID == other.mControllerID &&
-           strncmp(mControllerState.mControllerName, other.mControllerState.mControllerName, kVRControllerNameMaxLen) == 0 &&
+           memcmp(&mControllerState, &other.mControllerState, sizeof(VRControllerState)) == 0 &&
            mMappingType == other.mMappingType &&
-           mDisplayID == other.mDisplayID &&
-           mControllerState.mHand == other.mControllerState.mHand &&
-           mControllerState.mNumButtons == other.mControllerState.mNumButtons &&
-           mControllerState.mNumAxes == other.mControllerState.mNumAxes &&
-           mControllerState.mNumHaptics == other.mControllerState.mNumHaptics;
+           mDisplayID == other.mDisplayID;
   }
 
   bool operator!=(const VRControllerInfo& other) const {
     return !(*this == other);
   }
 };
 
 struct VRTelemetry
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -47,58 +47,54 @@ static const char* kShmemName = "/moz.ge
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
-static const uint32_t kNumExternalHaptcs = 1;
-
 int VRDisplayExternal::sPushIndex = 0;
 
 VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState)
   : VRDisplayHost(VRDeviceType::External)
   , mIsPresenting(false)
   , mLastSensorState{}
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
   mDisplayInfo.mDisplayState = aDisplayState;
 
   // default to an identity quaternion
-  mLastSensorState.orientation[3] = 1.0f;
+  mLastSensorState.pose.orientation[3] = 1.0f;
 }
 
 VRDisplayExternal::~VRDisplayExternal()
 {
   Destroy();
   MOZ_COUNT_DTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
 }
 
 void
 VRDisplayExternal::Destroy()
 {
   StopPresentation();
-
-  // TODO - Implement
 }
 
 void
 VRDisplayExternal::ZeroSensor()
 {
 }
 
 void
 VRDisplayExternal::Refresh()
 {
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
 
-  manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState);
+  manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState, mDisplayInfo.mControllerState);
 }
 
 VRHMDSensorState
 VRDisplayExternal::GetSensorState()
 {
   return mLastSensorState;
 }
 
@@ -232,53 +228,33 @@ VRDisplayExternal::SubmitFrame(const lay
 
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
   manager->PushState(&state, true);
   sPushIndex++;
 
   VRDisplayState displayState;
   memset(&displayState, 0, sizeof(VRDisplayState));
-  while (displayState.mLastSubmittedFrameId < aFrameId) {
+  if (manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState)) {
     if (manager->PullState(&displayState, &mLastSensorState)) {
       if (!displayState.mIsConnected) {
         // Service has shut down or hardware has been disconnected
         return false;
       }
     }
 #ifdef XP_WIN
     Sleep(0);
 #else
     sleep(0);
 #endif
   }
 
   return displayState.mLastSubmittedFrameSuccessful;
 }
 
-VRControllerExternal::VRControllerExternal(dom::GamepadHand aHand, uint32_t aDisplayID,
-                                       uint32_t aNumButtons, uint32_t aNumTriggers,
-                                       uint32_t aNumAxes, const nsCString& aId)
-  : VRControllerHost(VRDeviceType::External, aHand, aDisplayID)
-{
-  MOZ_COUNT_CTOR_INHERITED(VRControllerExternal, VRControllerHost);
-
-  VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.mControllerName, aId.BeginReading(), kVRControllerNameMaxLen);
-  state.mNumButtons = aNumButtons;
-  state.mNumAxes = aNumAxes;
-  state.mNumTriggers = aNumTriggers;
-  state.mNumHaptics = kNumExternalHaptcs;
-}
-
-VRControllerExternal::~VRControllerExternal()
-{
-  MOZ_COUNT_DTOR_INHERITED(VRControllerExternal, VRControllerHost);
-}
-
 VRSystemManagerExternal::VRSystemManagerExternal(VRExternalShmem* aAPIShmem /* = nullptr*/)
  : mExternalShmem(aAPIShmem)
 #if !defined(MOZ_WIDGET_ANDROID)
  , mSameProcess(aAPIShmem != nullptr)
 #endif
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
@@ -453,17 +429,16 @@ VRSystemManagerExternal::Destroy()
 }
 
 void
 VRSystemManagerExternal::Shutdown()
 {
   if (mDisplay) {
     mDisplay = nullptr;
   }
-  RemoveControllers();
   CloseShmem();
 #if defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
 #endif
 }
 
 void
 VRSystemManagerExternal::NotifyVSync()
@@ -533,22 +508,16 @@ VRSystemManagerExternal::GetIsPresenting
     VRDisplayInfo displayInfo(mDisplay->GetDisplayInfo());
     return displayInfo.GetPresentingGroups() != 0;
   }
 
   return false;
 }
 
 void
-VRSystemManagerExternal::HandleInput()
-{
-  // TODO - Implement This!
-}
-
-void
 VRSystemManagerExternal::VibrateHaptic(uint32_t aControllerIdx,
                                       uint32_t aHapticIndex,
                                       double aIntensity,
                                       double aDuration,
                                       const VRManagerPromise& aPromise)
 {
   // TODO - Implement this
 }
@@ -557,63 +526,77 @@ void
 VRSystemManagerExternal::StopVibrateHaptic(uint32_t aControllerIdx)
 {
   // TODO - Implement this
 }
 
 void
 VRSystemManagerExternal::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
 {
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
   aControllerResult.Clear();
-  for (uint32_t i = 0; i < mExternalController.Length(); ++i) {
-    aControllerResult.AppendElement(mExternalController[i]);
-  }
 }
 
 void
 VRSystemManagerExternal::ScanForControllers()
 {
-  // TODO - Implement this
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
+  if (mDisplay) {
+    mDisplay->Refresh();
+  }
+  return;
+}
+
+void
+VRSystemManagerExternal::HandleInput()
+{
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
+  if (mDisplay) {
+    mDisplay->Refresh();
+  }
+  return;
 }
 
 void
 VRSystemManagerExternal::RemoveControllers()
 {
-  // The controller count is changed, removing the existing gamepads first.
-  for (uint32_t i = 0; i < mExternalController.Length(); ++i) {
-    RemoveGamepad(i);
-  }
-  mExternalController.Clear();
-  mControllerCount = 0;
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
 }
 
 bool
-VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState /* = nullptr */)
-{
+VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
+                                   VRHMDSensorState* aSensorState /* = nullptr */,
+                                   VRControllerState* aControllerState /* = nullptr */) {
   bool success = false;
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
       memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
       if (aSensorState) {
         memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
       }
+      if (aControllerState) {
+        memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
+      }
       success = mExternalShmem->state.enumerationCompleted;
       pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
       mDoShutdown = aDisplayState->shutdown;
     }
 #else
     VRExternalShmem tmp;
     memcpy(&tmp, (void *)mExternalShmem, sizeof(VRExternalShmem));
     if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1 && tmp.state.enumerationCompleted) {
       memcpy(aDisplayState, &tmp.state.displayState, sizeof(VRDisplayState));
       if (aSensorState) {
         memcpy(aSensorState, &tmp.state.sensorState, sizeof(VRHMDSensorState));
       }
+      if (aControllerState) {
+        memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
+      }
       success = true;
     }
 #endif // defined(MOZ_WIDGET_ANDROID)
   }
 
   return success;
 }
 
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -41,41 +41,31 @@ protected:
   bool SubmitFrame(const layers::SurfaceDescriptor& aTexture,
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect) override;
 
 public:
   explicit VRDisplayExternal(const VRDisplayState& aDisplayState);
   void Refresh();
+  const VRControllerState& GetLastControllerState(uint32_t aStateIndex) const;
 protected:
   virtual ~VRDisplayExternal();
   void Destroy();
 
 private:
   bool PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
                             VRLayerTextureType* aTextureType,
                             VRLayerTextureHandle* aTextureHandle);
 
   VRTelemetry mTelemetry;
   bool mIsPresenting;
   VRHMDSensorState mLastSensorState;
 };
 
-class VRControllerExternal : public VRControllerHost
-{
-public:
-  explicit VRControllerExternal(dom::GamepadHand aHand, uint32_t aDisplayID, uint32_t aNumButtons,
-                              uint32_t aNumTriggers, uint32_t aNumAxes,
-                              const nsCString& aId);
-
-protected:
-  virtual ~VRControllerExternal();
-};
-
 } // namespace impl
 
 class VRSystemManagerExternal : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerExternal> Create(VRExternalShmem* aAPIShmem = nullptr);
 
   virtual void Destroy() override;
@@ -91,27 +81,28 @@ public:
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
                              double aIntensity,
                              double aDuration,
                              const VRManagerPromise& aPromise) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
-  bool PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState = nullptr);
+  bool PullState(VRDisplayState* aDisplayState,
+                 VRHMDSensorState* aSensorState = nullptr,
+                 VRControllerState* aControllerState = nullptr);
   void PushState(VRBrowserState* aBrowserState, const bool aNotifyCond = false);
 
 protected:
   explicit VRSystemManagerExternal(VRExternalShmem* aAPIShmem = nullptr);
   virtual ~VRSystemManagerExternal();
 
 private:
   // there can only be one
   RefPtr<impl::VRDisplayExternal> mDisplay;
-  nsTArray<RefPtr<impl::VRControllerExternal>> mExternalController;
 #if defined(XP_MACOSX)
   int mShmemFD;
 #elif defined(XP_WIN)
   HANDLE mShmemFile;
 #elif defined(MOZ_WIDGET_ANDROID)
   bool mDoShutdown;
   bool mExternalStructFailed;
 #endif
--- a/gfx/vr/gfxVROSVR.cpp
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -312,32 +312,32 @@ VRDisplayOSVR::GetSensorState()
   OSVR_ReturnCode ret =
     osvr_GetOrientationState(*m_iface, &timestamp, &orientation);
 
   result.timestamp = timestamp.seconds;
   result.inputFrameID = mDisplayInfo.mFrameId;
 
   if (ret == OSVR_RETURN_SUCCESS) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    result.orientation[0] = orientation.data[1];
-    result.orientation[1] = orientation.data[2];
-    result.orientation[2] = orientation.data[3];
-    result.orientation[3] = orientation.data[0];
+    result.pose.orientation[0] = orientation.data[1];
+    result.pose.orientation[1] = orientation.data[2];
+    result.pose.orientation[2] = orientation.data[3];
+    result.pose.orientation[3] = orientation.data[0];
   } else {
     // default to an identity quaternion
-    result.orientation[3] = 1.0f;
+    result.pose.orientation[3] = 1.0f;
   }
 
   OSVR_PositionState position;
   ret = osvr_GetPositionState(*m_iface, &timestamp, &position);
   if (ret == OSVR_RETURN_SUCCESS) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
-    result.position[0] = position.data[0];
-    result.position[1] = position.data[1];
-    result.position[2] = position.data[2];
+    result.pose.position[0] = position.data[0];
+    result.pose.position[1] = position.data[1];
+    result.pose.position[2] = position.data[2];
   }
 
   result.CalcViewMatrices(mHeadToEye);
 
   return result;
 }
 
 #if defined(XP_WIN)
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -947,17 +947,17 @@ VRDisplayOculus::GetSensorState()
     UpdateEyeParameters(headToEyeTransforms);
     double predictedFrameTime = 0.0f;
     if (gfxPrefs::VRPosePredictionEnabled()) {
       // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't use the result.
       // If we don't call it, the Oculus driver will spew out many warnings...
       predictedFrameTime = ovr_GetPredictedDisplayTime(mSession->Get(), 0);
     }
     result = GetSensorState(predictedFrameTime);
-    result.position[1] -= mEyeHeight;
+    result.pose.position[1] -= mEyeHeight;
     result.CalcViewMatrices(headToEyeTransforms);
   }
   result.inputFrameID = mDisplayInfo.mFrameId;
 
   return result;
 }
 
 VRHMDSensorState
@@ -968,51 +968,51 @@ VRDisplayOculus::GetSensorState(double a
   ovrTrackingState state = ovr_GetTrackingState(mSession->Get(), absTime, true);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
 
-    result.orientation[0] = pose.ThePose.Orientation.x;
-    result.orientation[1] = pose.ThePose.Orientation.y;
-    result.orientation[2] = pose.ThePose.Orientation.z;
-    result.orientation[3] = pose.ThePose.Orientation.w;
+    result.pose.orientation[0] = pose.ThePose.Orientation.x;
+    result.pose.orientation[1] = pose.ThePose.Orientation.y;
+    result.pose.orientation[2] = pose.ThePose.Orientation.z;
+    result.pose.orientation[3] = pose.ThePose.Orientation.w;
 
-    result.angularVelocity[0] = pose.AngularVelocity.x;
-    result.angularVelocity[1] = pose.AngularVelocity.y;
-    result.angularVelocity[2] = pose.AngularVelocity.z;
+    result.pose.angularVelocity[0] = pose.AngularVelocity.x;
+    result.pose.angularVelocity[1] = pose.AngularVelocity.y;
+    result.pose.angularVelocity[2] = pose.AngularVelocity.z;
 
     result.flags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
 
-    result.angularAcceleration[0] = pose.AngularAcceleration.x;
-    result.angularAcceleration[1] = pose.AngularAcceleration.y;
-    result.angularAcceleration[2] = pose.AngularAcceleration.z;
+    result.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
+    result.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
+    result.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
   } else {
     // default to an identity quaternion
-    result.orientation[3] = 1.0f;
+    result.pose.orientation[3] = 1.0f;
   }
 
   if (state.StatusFlags & ovrStatus_PositionTracked) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
 
-    result.position[0] = pose.ThePose.Position.x;
-    result.position[1] = pose.ThePose.Position.y;
-    result.position[2] = pose.ThePose.Position.z;
+    result.pose.position[0] = pose.ThePose.Position.x;
+    result.pose.position[1] = pose.ThePose.Position.y;
+    result.pose.position[2] = pose.ThePose.Position.z;
 
-    result.linearVelocity[0] = pose.LinearVelocity.x;
-    result.linearVelocity[1] = pose.LinearVelocity.y;
-    result.linearVelocity[2] = pose.LinearVelocity.z;
+    result.pose.linearVelocity[0] = pose.LinearVelocity.x;
+    result.pose.linearVelocity[1] = pose.LinearVelocity.y;
+    result.pose.linearVelocity[2] = pose.LinearVelocity.z;
 
     result.flags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
 
-    result.linearAcceleration[0] = pose.LinearAcceleration.x;
-    result.linearAcceleration[1] = pose.LinearAcceleration.y;
-    result.linearAcceleration[2] = pose.LinearAcceleration.z;
+    result.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
+    result.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
+    result.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
   }
   result.flags |= VRDisplayCapabilityFlags::Cap_External;
   result.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
   result.flags |= VRDisplayCapabilityFlags::Cap_Present;
 
   return result;
 }
 
@@ -1325,27 +1325,27 @@ VRControllerOculus::VRControllerOculus(d
     case dom::GamepadHand::Right:
       touchID = "Oculus Touch (Right)";
       break;
     default:
       MOZ_ASSERT(false);
       break;
   }
 
-  strncpy(state.mControllerName, touchID, kVRControllerNameMaxLen);
+  strncpy(state.controllerName, touchID, kVRControllerNameMaxLen);
 
   MOZ_ASSERT(kNumOculusButton ==
              static_cast<uint32_t>(OculusLeftControllerButtonType::NumButtonType)
              && kNumOculusButton ==
              static_cast<uint32_t>(OculusRightControllerButtonType::NumButtonType));
 
-  state.mNumButtons = kNumOculusButton;
-  state.mNumAxes = static_cast<uint32_t>(
+  state.numButtons = kNumOculusButton;
+  state.numAxes = static_cast<uint32_t>(
                    OculusControllerAxisType::NumVRControllerAxisType);
-  state.mNumHaptics = kNumOculusHaptcs;
+  state.numHaptics = kNumOculusHaptcs;
 }
 
 float
 VRControllerOculus::GetAxisMove(uint32_t aAxis)
 {
   return mAxisMove[aAxis];
 }
 
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -282,34 +282,34 @@ VRDisplayOpenVR::GetSensorState()
     memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
     m.Transpose();
 
     gfx::Quaternion rot;
     rot.SetFromRotationMatrix(m);
     rot.Invert();
 
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    result.orientation[0] = rot.x;
-    result.orientation[1] = rot.y;
-    result.orientation[2] = rot.z;
-    result.orientation[3] = rot.w;
-    result.angularVelocity[0] = pose.vAngularVelocity.v[0];
-    result.angularVelocity[1] = pose.vAngularVelocity.v[1];
-    result.angularVelocity[2] = pose.vAngularVelocity.v[2];
+    result.pose.orientation[0] = rot.x;
+    result.pose.orientation[1] = rot.y;
+    result.pose.orientation[2] = rot.z;
+    result.pose.orientation[3] = rot.w;
+    result.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+    result.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+    result.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
 
     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
-    result.position[0] = m._41;
-    result.position[1] = m._42;
-    result.position[2] = m._43;
-    result.linearVelocity[0] = pose.vVelocity.v[0];
-    result.linearVelocity[1] = pose.vVelocity.v[1];
-    result.linearVelocity[2] = pose.vVelocity.v[2];
+    result.pose.position[0] = m._41;
+    result.pose.position[1] = m._42;
+    result.pose.position[2] = m._43;
+    result.pose.linearVelocity[0] = pose.vVelocity.v[0];
+    result.pose.linearVelocity[1] = pose.vVelocity.v[1];
+    result.pose.linearVelocity[2] = pose.vVelocity.v[2];
   } else {
     // default to an identity quaternion
-    result.orientation[3] = 1.0f;
+    result.pose.orientation[3] = 1.0f;
   }
 
   result.CalcViewMatrices(headToEyeTransforms);
   result.inputFrameID = mDisplayInfo.mFrameId;
   return result;
 }
 
 void
@@ -433,21 +433,20 @@ VRControllerOpenVR::VRControllerOpenVR(d
   : VRControllerHost(VRDeviceType::OpenVR, aHand, aDisplayID)
   , mTrackedIndex(0)
   , mVibrateThread(nullptr)
   , mIsVibrateStopped(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
 
   VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.mControllerName, aId.BeginReading(), kVRControllerNameMaxLen);
-  state.mNumButtons = aNumButtons;
-  state.mNumAxes = aNumAxes;
-  state.mNumTriggers = aNumTriggers;
-  state.mNumHaptics = kNumOpenVRHaptcs;
+  strncpy(state.controllerName, aId.BeginReading(), kVRControllerNameMaxLen);
+  state.numButtons = aNumButtons;
+  state.numAxes = aNumAxes;
+  state.numHaptics = kNumOpenVRHaptcs;
 }
 
 VRControllerOpenVR::~VRControllerOpenVR()
 {
   ShutdownVibrateHapticThread();
   MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
 }
 
@@ -461,41 +460,41 @@ uint32_t
 VRControllerOpenVR::GetTrackedIndex()
 {
   return mTrackedIndex;
 }
 
 float
 VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
 {
-  return mControllerInfo.mControllerState.mAxisValue[aAxis];
+  return mControllerInfo.mControllerState.axisValue[aAxis];
 }
 
 void
 VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
 {
-  mControllerInfo.mControllerState.mAxisValue[aAxis] = aValue;
+  mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
 }
 
 void
 VRControllerOpenVR::SetTrigger(uint32_t aButton, float aValue)
 {
-  mControllerInfo.mControllerState.mTriggerValue[aButton] = aValue;
+  mControllerInfo.mControllerState.triggerValue[aButton] = aValue;
 }
 
 float
 VRControllerOpenVR::GetTrigger(uint32_t aButton)
 {
-  return mControllerInfo.mControllerState.mTriggerValue[aButton];
+  return mControllerInfo.mControllerState.triggerValue[aButton];
 }
 
 void
 VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
 {
-  mControllerInfo.mControllerState.mHand = aHand;
+  mControllerInfo.mControllerState.hand = aHand;
 }
 
 void
 VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
                                         uint32_t aHapticIndex,
                                         double aIntensity,
                                         double aDuration,
                                         uint64_t aVibrateIndex,
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -99,31 +99,31 @@ VRDisplayPuppet::VRDisplayPuppet()
   state.mSittingToStandingTransform[12] = 0.0f;
   state.mSittingToStandingTransform[13] = 0.75f;
   state.mSittingToStandingTransform[14] = 0.0f;
   state.mSittingToStandingTransform[15] = 1.0f;
 
   gfx::Quaternion rot;
 
   mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-  mSensorState.orientation[0] = rot.x;
-  mSensorState.orientation[1] = rot.y;
-  mSensorState.orientation[2] = rot.z;
-  mSensorState.orientation[3] = rot.w;
-  mSensorState.angularVelocity[0] = 0.0f;
-  mSensorState.angularVelocity[1] = 0.0f;
-  mSensorState.angularVelocity[2] = 0.0f;
+  mSensorState.pose.orientation[0] = rot.x;
+  mSensorState.pose.orientation[1] = rot.y;
+  mSensorState.pose.orientation[2] = rot.z;
+  mSensorState.pose.orientation[3] = rot.w;
+  mSensorState.pose.angularVelocity[0] = 0.0f;
+  mSensorState.pose.angularVelocity[1] = 0.0f;
+  mSensorState.pose.angularVelocity[2] = 0.0f;
 
   mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
-  mSensorState.position[0] = 0.0f;
-  mSensorState.position[1] = 0.0f;
-  mSensorState.position[2] = 0.0f;
-  mSensorState.linearVelocity[0] = 0.0f;
-  mSensorState.linearVelocity[1] = 0.0f;
-  mSensorState.linearVelocity[2] = 0.0f;
+  mSensorState.pose.position[0] = 0.0f;
+  mSensorState.pose.position[1] = 0.0f;
+  mSensorState.pose.position[2] = 0.0f;
+  mSensorState.pose.linearVelocity[0] = 0.0f;
+  mSensorState.pose.linearVelocity[1] = 0.0f;
+  mSensorState.pose.linearVelocity[2] = 0.0f;
 }
 
 VRDisplayPuppet::~VRDisplayPuppet()
 {
   MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayLocal);
 }
 
 void
@@ -580,20 +580,20 @@ VRDisplayPuppet::Refresh()
 
 VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand, uint32_t aDisplayID)
   : VRControllerHost(VRDeviceType::Puppet, aHand, aDisplayID)
   , mButtonPressState(0)
   , mButtonTouchState(0)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerPuppet, VRControllerHost);
   VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.mControllerName, "Puppet Gamepad", kVRControllerNameMaxLen);
-  state.mNumButtons = kNumPuppetButtonMask;
-  state.mNumAxes = kNumPuppetAxis;
-  state.mNumHaptics = kNumPuppetHaptcs;
+  strncpy(state.controllerName, "Puppet Gamepad", kVRControllerNameMaxLen);
+  state.numButtons = kNumPuppetButtonMask;
+  state.numAxes = kNumPuppetAxis;
+  state.numHaptics = kNumPuppetHaptcs;
 }
 
 VRControllerPuppet::~VRControllerPuppet()
 {
   MOZ_COUNT_DTOR_INHERITED(VRControllerPuppet, VRControllerHost);
 }
 
 void
@@ -667,23 +667,23 @@ const dom::GamepadPoseState&
 VRControllerPuppet::GetPoseMoveState()
 {
   return mPoseState;
 }
 
 float
 VRControllerPuppet::GetAxisMove(uint32_t aAxis)
 {
-  return mControllerInfo.mControllerState.mAxisValue[aAxis];
+  return mControllerInfo.mControllerState.axisValue[aAxis];
 }
 
 void
 VRControllerPuppet::SetAxisMove(uint32_t aAxis, float aValue)
 {
-  mControllerInfo.mControllerState.mAxisValue[aAxis] = aValue;
+  mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
 }
 
 VRSystemManagerPuppet::VRSystemManagerPuppet()
   : mPuppetDisplayCount(0)
   , mPuppetDisplayInfo{}
   , mPuppetDisplaySensorState{}
 {
 }
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -39,16 +39,17 @@ void ReleaseVRManagerParentSingleton() {
 
 VRManagerChild::VRManagerChild()
   : mDisplaysInitialized(false)
   , mMessageLoop(MessageLoop::current())
   , mFrameRequestCallbackCounter(0)
   , mBackend(layers::LayersBackend::LAYERS_NONE)
   , mPromiseID(0)
   , mVRMockDisplay(nullptr)
+  , mLastControllerState{}
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mStartTimeStamp = TimeStamp::Now();
 }
 
 VRManagerChild::~VRManagerChild()
 {
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -134,16 +134,17 @@ private:
   nsTArray<RefPtr<dom::VREventObserver>> mListeners;
 
   layers::LayersBackend mBackend;
   RefPtr<layers::SyncObjectClient> mSyncObject;
   nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mGamepadPromiseList;
   uint32_t mPromiseID;
   nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mPromiseList;
   RefPtr<dom::VRMockDisplay> mVRMockDisplay;
+  VRControllerState mLastControllerState[kVRControllerMaxCount];
 
   DISALLOW_COPY_AND_ASSIGN(VRManagerChild);
 };
 
 } // namespace mozilla
 } // namespace gfx
 
 #endif // MOZILLA_GFX_VR_VRMANAGERCHILD_H
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_gfx_vr_VRMessageUtils_h
 #define mozilla_gfx_vr_VRMessageUtils_h
 
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/ArrayUtils.h"
 #include "mozilla/GfxMessageUtils.h"
 #include "VRManager.h"
 
 #include "gfxVR.h"
 
 namespace IPC {
 
 template<>
@@ -104,51 +105,56 @@ struct ParamTraits<mozilla::gfx::VRDispl
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mType);
     WriteParam(aMsg, aParam.mDisplayID);
     WriteParam(aMsg, aParam.mPresentingGroups);
     WriteParam(aMsg, aParam.mGroupMask);
     WriteParam(aMsg, aParam.mFrameId);
     WriteParam(aMsg, aParam.mDisplayState);
-    for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.mLastSensorState); i++) {
       WriteParam(aMsg, aParam.mLastSensorState[i]);
     }
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.mControllerState); i++) {
+      WriteParam(aMsg, aParam.mControllerState[i]);
+    }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->mType)) ||
         !ReadParam(aMsg, aIter, &(aResult->mDisplayID)) ||
         !ReadParam(aMsg, aIter, &(aResult->mPresentingGroups)) ||
         !ReadParam(aMsg, aIter, &(aResult->mGroupMask)) ||
         !ReadParam(aMsg, aIter, &(aResult->mFrameId)) ||
         !ReadParam(aMsg, aIter, &(aResult->mDisplayState))) {
       return false;
     }
-    for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->mLastSensorState); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mLastSensorState[i]))) {
         return false;
       }
     }
-
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->mControllerState); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mControllerState[i]))) {
+        return false;
+      }
+    }
     return true;
   }
 };
 
+
 template <>
-struct ParamTraits<mozilla::gfx::VRHMDSensorState>
+struct ParamTraits<mozilla::gfx::VRPose>
 {
-  typedef mozilla::gfx::VRHMDSensorState paramType;
+  typedef mozilla::gfx::VRPose paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.timestamp);
-    WriteParam(aMsg, aParam.inputFrameID);
-    WriteParam(aMsg, aParam.flags);
     WriteParam(aMsg, aParam.orientation[0]);
     WriteParam(aMsg, aParam.orientation[1]);
     WriteParam(aMsg, aParam.orientation[2]);
     WriteParam(aMsg, aParam.orientation[3]);
     WriteParam(aMsg, aParam.position[0]);
     WriteParam(aMsg, aParam.position[1]);
     WriteParam(aMsg, aParam.position[2]);
     WriteParam(aMsg, aParam.angularVelocity[0]);
@@ -158,30 +164,21 @@ struct ParamTraits<mozilla::gfx::VRHMDSe
     WriteParam(aMsg, aParam.angularAcceleration[1]);
     WriteParam(aMsg, aParam.angularAcceleration[2]);
     WriteParam(aMsg, aParam.linearVelocity[0]);
     WriteParam(aMsg, aParam.linearVelocity[1]);
     WriteParam(aMsg, aParam.linearVelocity[2]);
     WriteParam(aMsg, aParam.linearAcceleration[0]);
     WriteParam(aMsg, aParam.linearAcceleration[1]);
     WriteParam(aMsg, aParam.linearAcceleration[2]);
-    for (int i=0; i < 16; i++) {
-      WriteParam(aMsg, aParam.leftViewMatrix[i]);
-    }
-    for (int i=0; i < 16; i++) {
-      WriteParam(aMsg, aParam.rightViewMatrix[i]);
-    }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->timestamp)) ||
-        !ReadParam(aMsg, aIter, &(aResult->inputFrameID)) ||
-        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
-        !ReadParam(aMsg, aIter, &(aResult->orientation[0])) ||
+    if (!ReadParam(aMsg, aIter, &(aResult->orientation[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[3])) ||
         !ReadParam(aMsg, aIter, &(aResult->position[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->position[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->position[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->angularVelocity[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->angularVelocity[1])) ||
@@ -192,22 +189,53 @@ struct ParamTraits<mozilla::gfx::VRHMDSe
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2]))) {
       return false;
     }
-    for (int i=0; i < 16; i++) {
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRHMDSensorState>
+{
+  typedef mozilla::gfx::VRHMDSensorState paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.timestamp);
+    WriteParam(aMsg, aParam.inputFrameID);
+    WriteParam(aMsg, aParam.flags);
+    WriteParam(aMsg, aParam.pose);
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.leftViewMatrix); i++) {
+      WriteParam(aMsg, aParam.leftViewMatrix[i]);
+    }
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.rightViewMatrix); i++) {
+      WriteParam(aMsg, aParam.rightViewMatrix[i]);
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->timestamp)) ||
+        !ReadParam(aMsg, aIter, &(aResult->inputFrameID)) ||
+        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
+        !ReadParam(aMsg, aIter, &(aResult->pose))) {
+      return false;
+    }
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->leftViewMatrix); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->leftViewMatrix[i]))) {
         return false;
       }
     }
-    for (int i=0; i < 16; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->rightViewMatrix); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->rightViewMatrix[i]))) {
         return false;
       }
     }
     return true;
   }
 };
 
@@ -241,55 +269,63 @@ struct ParamTraits<mozilla::gfx::VRField
 template <>
 struct ParamTraits<mozilla::gfx::VRControllerState>
 {
   typedef mozilla::gfx::VRControllerState paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     nsCString controllerName;
-    controllerName.Assign(aParam.mControllerName); // FINDME!! HACK! - Bounds checking?
+    controllerName.Assign(aParam.controllerName); // FINDME!! HACK! - Bounds checking?
     WriteParam(aMsg, controllerName);
-    WriteParam(aMsg, aParam.mNumButtons);
-    WriteParam(aMsg, aParam.mNumAxes);
-    WriteParam(aMsg, aParam.mNumTriggers);
-    WriteParam(aMsg, aParam.mNumHaptics);
-    WriteParam(aMsg, aParam.mButtonPressed);
-    WriteParam(aMsg, aParam.mButtonTouched);
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
-      WriteParam(aMsg, aParam.mAxisValue[i]);
+    WriteParam(aMsg, aParam.hand);
+    WriteParam(aMsg, aParam.numButtons);
+    WriteParam(aMsg, aParam.numAxes);
+    WriteParam(aMsg, aParam.numHaptics);
+    WriteParam(aMsg, aParam.buttonPressed);
+    WriteParam(aMsg, aParam.buttonTouched);
+    WriteParam(aMsg, aParam.flags);
+    WriteParam(aMsg, aParam.pose);
+    WriteParam(aMsg, aParam.isPositionValid);
+    WriteParam(aMsg, aParam.isOrientationValid);
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.axisValue); i++) {
+      WriteParam(aMsg, aParam.axisValue[i]);
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
-      WriteParam(aMsg, aParam.mTriggerValue[i]);
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.triggerValue); i++) {
+      WriteParam(aMsg, aParam.triggerValue[i]);
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     nsCString controllerName;
     if (!ReadParam(aMsg, aIter, &(controllerName)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumButtons)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumAxes)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumTriggers)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumHaptics)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mButtonPressed)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mButtonTouched))) {
+        !ReadParam(aMsg, aIter, &(aResult->hand)) ||
+        !ReadParam(aMsg, aIter, &(aResult->numButtons)) ||
+        !ReadParam(aMsg, aIter, &(aResult->numAxes)) ||
+        !ReadParam(aMsg, aIter, &(aResult->numHaptics)) ||
+        !ReadParam(aMsg, aIter, &(aResult->buttonPressed)) ||
+        !ReadParam(aMsg, aIter, &(aResult->buttonTouched)) ||
+        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
+        !ReadParam(aMsg, aIter, &(aResult->pose)) ||
+        !ReadParam(aMsg, aIter, &(aResult->isPositionValid)) ||
+        !ReadParam(aMsg, aIter, &(aResult->isOrientationValid))) {
       return false;
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
-      if (!ReadParam(aMsg, aIter, &(aResult->mAxisValue[i]))) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->axisValue); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->axisValue[i]))) {
         return false;
       }
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
-      if (!ReadParam(aMsg, aIter, &(aResult->mTriggerValue[i]))) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->triggerValue); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->triggerValue[i]))) {
         return false;
       }
     }
-    strncpy(aResult->mControllerName, controllerName.BeginReading(), mozilla::gfx::kVRControllerNameMaxLen); // FINDME! TODO! HACK!  Safe? Better way?
+    strncpy(aResult->controllerName, controllerName.BeginReading(), mozilla::gfx::kVRControllerNameMaxLen); // FINDME! TODO! HACK!  Safe? Better way?
 
     return true;
   }
 };
 
 template <>
 struct ParamTraits<mozilla::gfx::VRControllerInfo>
 {
--- a/gfx/vr/service/OpenVRSession.cpp
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -1,55 +1,130 @@
 #include "OpenVRSession.h"
+#include "gfxPrefs.h"
 
 #if defined(XP_WIN)
 #include <d3d11.h>
 #include "mozilla/gfx/DeviceManagerDx.h"
 #endif // defined(XP_WIN)
 
-#if defined(MOZILLA_INTERNAL_API)
 #include "mozilla/dom/GamepadEventTypes.h"
 #include "mozilla/dom/GamepadBinding.h"
-#endif
 
 #if !defined(M_PI)
 #define M_PI 3.14159265358979323846264338327950288
 #endif
 
 #define BTN_MASK_FROM_ID(_id) \
   ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
 
+static const uint32_t kNumOpenVRHaptcs = 1;
+
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gfx {
 
+namespace {
+
+dom::GamepadHand
+GetControllerHandFromControllerRole(::vr::ETrackedControllerRole aRole)
+{
+  dom::GamepadHand hand;
+
+  switch(aRole) {
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_OptOut:
+      hand = dom::GamepadHand::_empty;
+      break;
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
+      hand = dom::GamepadHand::Left;
+      break;
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
+      hand = dom::GamepadHand::Right;
+      break;
+    default:
+      hand = dom::GamepadHand::_empty;
+      MOZ_ASSERT(false);
+      break;
+  }
+
+  return hand;
+}
+
+
+void
+UpdateButton(VRControllerState& aState, const ::vr::VRControllerState_t& aControllerState, uint32_t aButtonIndex, uint64_t aButtonMask)
+{
+  uint64_t mask = (1ULL << aButtonIndex);
+  if ((aControllerState.ulButtonPressed & aButtonMask) == 0) {
+    // not pressed
+    aState.buttonPressed &= ~mask;
+    aState.triggerValue[aButtonIndex] = 0.0f;
+  } else {
+    // pressed
+    aState.buttonPressed |= mask;
+    aState.triggerValue[aButtonIndex] = 1.0f;
+  }
+  if ((aControllerState.ulButtonTouched & aButtonMask) == 0) {
+    // not touched
+    aState.buttonTouched &= ~mask;
+  } else {
+    // touched
+    aState.buttonTouched |= mask;
+  }
+}
+
+void
+UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex, float aValue, float aThreshold)
+{
+  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
+  // We prefer to let developers to set their own threshold for the adjustment.
+  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
+  // we just check the button value is larger than the threshold value or not.
+  uint64_t mask = (1ULL << aButtonIndex);
+  aState.triggerValue[aButtonIndex] = aValue;
+  if (aValue > aThreshold) {
+    aState.buttonPressed |= mask;
+    aState.buttonTouched |= mask;
+  } else {
+    aState.buttonPressed &= ~mask;
+    aState.buttonTouched &= ~mask;
+  }
+}
+
+}; // anonymous namespace
+
 OpenVRSession::OpenVRSession()
   : VRSession()
   , mVRSystem(nullptr)
   , mVRChaperone(nullptr)
   , mVRCompositor(nullptr)
+  , mControllerDeviceIndex{0}
   , mShouldQuit(false)
+  , mIsWindowsMR(false)
 {
 }
 
 OpenVRSession::~OpenVRSession()
 {
   Shutdown();
 }
 
 bool
 OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState)
 {
   if (mVRSystem != nullptr) {
     // Already initialized
     return true;
   }
+  if (!::vr::VR_IsRuntimeInstalled()) {
+    return false;
+  }
   if (!::vr::VR_IsHmdPresent()) {
-    fprintf(stderr, "No HMD detected, VR_IsHmdPresent returned false.\n");
     return false;
   }
 
   ::vr::HmdError err;
 
   ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
   if (err) {
     return false;
@@ -138,131 +213,131 @@ OpenVRSession::InitState(VRSystemState& 
   }
 
   uint32_t w, h;
   mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
   state.mEyeResolution.width = w;
   state.mEyeResolution.height = h;
 
   // default to an identity quaternion
-  aSystemState.sensorState.orientation[3] = 1.0f;
+  aSystemState.sensorState.pose.orientation[3] = 1.0f;
 
   UpdateStageParameters(state);
-  UpdateEyeParameters(state);
+  UpdateEyeParameters(aSystemState);
 
   VRHMDSensorState& sensorState = aSystemState.sensorState;
   sensorState.flags = (VRDisplayCapabilityFlags)(
     (int)VRDisplayCapabilityFlags::Cap_Orientation |
     (int)VRDisplayCapabilityFlags::Cap_Position);
-  sensorState.orientation[3] = 1.0f; // Default to an identity quaternion
+  sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
 
   return true;
 }
 
 void
-OpenVRSession::UpdateStageParameters(VRDisplayState& state)
+OpenVRSession::UpdateStageParameters(VRDisplayState& aState)
 {
   float sizeX = 0.0f;
   float sizeZ = 0.0f;
   if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
     ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
-    state.mStageSize.width = sizeX;
-    state.mStageSize.height = sizeZ;
+    aState.mStageSize.width = sizeX;
+    aState.mStageSize.height = sizeZ;
 
-    state.mSittingToStandingTransform[0] = t.m[0][0];
-    state.mSittingToStandingTransform[1] = t.m[1][0];
-    state.mSittingToStandingTransform[2] = t.m[2][0];
-    state.mSittingToStandingTransform[3] = 0.0f;
+    aState.mSittingToStandingTransform[0] = t.m[0][0];
+    aState.mSittingToStandingTransform[1] = t.m[1][0];
+    aState.mSittingToStandingTransform[2] = t.m[2][0];
+    aState.mSittingToStandingTransform[3] = 0.0f;
 
-    state.mSittingToStandingTransform[4] = t.m[0][1];
-    state.mSittingToStandingTransform[5] = t.m[1][1];
-    state.mSittingToStandingTransform[6] = t.m[2][1];
-    state.mSittingToStandingTransform[7] = 0.0f;
+    aState.mSittingToStandingTransform[4] = t.m[0][1];
+    aState.mSittingToStandingTransform[5] = t.m[1][1];
+    aState.mSittingToStandingTransform[6] = t.m[2][1];
+    aState.mSittingToStandingTransform[7] = 0.0f;
 
-    state.mSittingToStandingTransform[8] = t.m[0][2];
-    state.mSittingToStandingTransform[9] = t.m[1][2];
-    state.mSittingToStandingTransform[10] = t.m[2][2];
-    state.mSittingToStandingTransform[11] = 0.0f;
+    aState.mSittingToStandingTransform[8] = t.m[0][2];
+    aState.mSittingToStandingTransform[9] = t.m[1][2];
+    aState.mSittingToStandingTransform[10] = t.m[2][2];
+    aState.mSittingToStandingTransform[11] = 0.0f;
 
-    state.mSittingToStandingTransform[12] = t.m[0][3];
-    state.mSittingToStandingTransform[13] = t.m[1][3];
-    state.mSittingToStandingTransform[14] = t.m[2][3];
-    state.mSittingToStandingTransform[15] = 1.0f;
+    aState.mSittingToStandingTransform[12] = t.m[0][3];
+    aState.mSittingToStandingTransform[13] = t.m[1][3];
+    aState.mSittingToStandingTransform[14] = t.m[2][3];
+    aState.mSittingToStandingTransform[15] = 1.0f;
   } else {
     // If we fail, fall back to reasonable defaults.
     // 1m x 1m space, 0.75m high in seated position
-
-    state.mStageSize.width = 1.0f;
-    state.mStageSize.height = 1.0f;
+    aState.mStageSize.width = 1.0f;
+    aState.mStageSize.height = 1.0f;
 
-    state.mSittingToStandingTransform[0] = 1.0f;
-    state.mSittingToStandingTransform[1] = 0.0f;
-    state.mSittingToStandingTransform[2] = 0.0f;
-    state.mSittingToStandingTransform[3] = 0.0f;
+    aState.mSittingToStandingTransform[0] = 1.0f;
+    aState.mSittingToStandingTransform[1] = 0.0f;
+    aState.mSittingToStandingTransform[2] = 0.0f;
+    aState.mSittingToStandingTransform[3] = 0.0f;
 
-    state.mSittingToStandingTransform[4] = 0.0f;
-    state.mSittingToStandingTransform[5] = 1.0f;
-    state.mSittingToStandingTransform[6] = 0.0f;
-    state.mSittingToStandingTransform[7] = 0.0f;
+    aState.mSittingToStandingTransform[4] = 0.0f;
+    aState.mSittingToStandingTransform[5] = 1.0f;
+    aState.mSittingToStandingTransform[6] = 0.0f;
+    aState.mSittingToStandingTransform[7] = 0.0f;
 
-    state.mSittingToStandingTransform[8] = 0.0f;
-    state.mSittingToStandingTransform[9] = 0.0f;
-    state.mSittingToStandingTransform[10] = 1.0f;
-    state.mSittingToStandingTransform[11] = 0.0f;
+    aState.mSittingToStandingTransform[8] = 0.0f;
+    aState.mSittingToStandingTransform[9] = 0.0f;
+    aState.mSittingToStandingTransform[10] = 1.0f;
+    aState.mSittingToStandingTransform[11] = 0.0f;
 
-    state.mSittingToStandingTransform[12] = 0.0f;
-    state.mSittingToStandingTransform[13] = 0.75f;
-    state.mSittingToStandingTransform[14] = 0.0f;
-    state.mSittingToStandingTransform[15] = 1.0f;
+    aState.mSittingToStandingTransform[12] = 0.0f;
+    aState.mSittingToStandingTransform[13] = 0.75f;
+    aState.mSittingToStandingTransform[14] = 0.0f;
+    aState.mSittingToStandingTransform[15] = 1.0f;
   }
 }
 
 void
-OpenVRSession::UpdateEyeParameters(VRDisplayState& state, gfx::Matrix4x4* headToEyeTransforms /* = nullptr */)
+OpenVRSession::UpdateEyeParameters(VRSystemState& aState)
 {
+  // This must be called every frame in order to
+  // account for continuous adjustments to ipd.
+  gfx::Matrix4x4 headToEyeTransforms[2];
+
   for (uint32_t eye = 0; eye < 2; ++eye) {
     ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
-    state.mEyeTranslation[eye].x = eyeToHead.m[0][3];
-    state.mEyeTranslation[eye].y = eyeToHead.m[1][3];
-    state.mEyeTranslation[eye].z = eyeToHead.m[2][3];
+    aState.displayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
+    aState.displayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
+    aState.displayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
 
     float left, right, up, down;
     mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right, &up, &down);
-    state.mEyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
-    state.mEyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
-    state.mEyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
-    state.mEyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
 
-    if (headToEyeTransforms) {
-      Matrix4x4 pose;
-      // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
-      // because of its arrangement, we can copy the 12 elements in and
-      // then transpose them to the right place.
-      memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
-      pose.Transpose();
-      pose.Invert();
-      headToEyeTransforms[eye] = pose;
-    }
+    Matrix4x4 pose;
+    // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
+    // because of its arrangement, we can copy the 12 elements in and
+    // then transpose them to the right place.
+    memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
+    pose.Transpose();
+    pose.Invert();
+    headToEyeTransforms[eye] = pose;
   }
+  aState.sensorState.CalcViewMatrices(headToEyeTransforms);
 }
 
 void
-OpenVRSession::GetSensorState(VRSystemState& state)
+OpenVRSession::UpdateHeadsetPose(VRSystemState& aState)
 {
   const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
   ::vr::TrackedDevicePose_t poses[posesSize];
   // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
   mVRCompositor->WaitGetPoses(nullptr, 0, poses, posesSize);
-  gfx::Matrix4x4 headToEyeTransforms[2];
-  UpdateEyeParameters(state.displayState, headToEyeTransforms);
 
   ::vr::Compositor_FrameTiming timing;
   timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
   if (mVRCompositor->GetFrameTiming(&timing)) {
-    state.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
+    aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
   } else {
     // This should not happen, but log it just in case
     fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
   }
 
   if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
     poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
     poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
@@ -276,49 +351,428 @@ OpenVRSession::GetSensorState(VRSystemSt
     // pull out a Quaternion.
     memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
     m.Transpose();
 
     gfx::Quaternion rot;
     rot.SetFromRotationMatrix(m);
     rot.Invert();
 
-    state.sensorState.flags = (VRDisplayCapabilityFlags)((int)state.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Orientation);
-    state.sensorState.orientation[0] = rot.x;
-    state.sensorState.orientation[1] = rot.y;
-    state.sensorState.orientation[2] = rot.z;
-    state.sensorState.orientation[3] = rot.w;
-    state.sensorState.angularVelocity[0] = pose.vAngularVelocity.v[0];
-    state.sensorState.angularVelocity[1] = pose.vAngularVelocity.v[1];
-    state.sensorState.angularVelocity[2] = pose.vAngularVelocity.v[2];
+    aState.sensorState.flags = (VRDisplayCapabilityFlags)((int)aState.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Orientation);
+    aState.sensorState.pose.orientation[0] = rot.x;
+    aState.sensorState.pose.orientation[1] = rot.y;
+    aState.sensorState.pose.orientation[2] = rot.z;
+    aState.sensorState.pose.orientation[3] = rot.w;
+    aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+    aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+    aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
+
+    aState.sensorState.flags =(VRDisplayCapabilityFlags)((int)aState.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Position);
+    aState.sensorState.pose.position[0] = m._41;
+    aState.sensorState.pose.position[1] = m._42;
+    aState.sensorState.pose.position[2] = m._43;
+    aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
+    aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
+    aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
+  }
+}
+
+void
+OpenVRSession::EnumerateControllers(VRSystemState& aState)
+{
+  MOZ_ASSERT(mVRSystem);
+
+  bool controllerPresent[kVRControllerMaxCount] = { false };
+
+  // Basically, we would have HMDs in the tracked devices,
+  // but we are just interested in the controllers.
+  for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
+       trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
+
+    if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
+      continue;
+    }
+
+    const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
+                                                 GetTrackedDeviceClass(trackedDevice);
+    if (deviceType != ::vr::TrackedDeviceClass_Controller
+        && deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
+      continue;
+    }
+
+    uint32_t stateIndex = 0;
+    uint32_t firstEmptyIndex = kVRControllerMaxCount;
+
+    // Find the existing controller
+    for (stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+      if (mControllerDeviceIndex[stateIndex] == 0 && firstEmptyIndex == kVRControllerMaxCount) {
+        firstEmptyIndex = stateIndex;
+      }
+      if (mControllerDeviceIndex[stateIndex] == trackedDevice) {
+        break;
+      }
+    }
+    if (stateIndex == kVRControllerMaxCount) {
+      // This is a new controller, let's add it
+      if (firstEmptyIndex == kVRControllerMaxCount) {
+        NS_WARNING("OpenVR - Too many controllers, need to increase kVRControllerMaxCount.");
+        continue;
+      }
+      stateIndex = firstEmptyIndex;
+      mControllerDeviceIndex[stateIndex] = trackedDevice;
+      VRControllerState& controllerState = aState.controllerState[stateIndex];
+      uint32_t numButtons = 0;
+      uint32_t numAxes = 0;
 
-    state.sensorState.flags =(VRDisplayCapabilityFlags)((int)state.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Position);
-    state.sensorState.position[0] = m._41;
-    state.sensorState.position[1] = m._42;
-    state.sensorState.position[2] = m._43;
-    state.sensorState.linearVelocity[0] = pose.vVelocity.v[0];
-    state.sensorState.linearVelocity[1] = pose.vVelocity.v[1];
-    state.sensorState.linearVelocity[2] = pose.vVelocity.v[2];
+      // Scan the axes that the controllers support
+      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
+        const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
+                                      static_cast<vr::TrackedDeviceProperty>(
+                                      ::vr::Prop_Axis0Type_Int32 + j));
+        switch (supportAxis) {
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
+            numAxes += 2; // It has x and y axes.
+            ++numButtons;
+            break;
+          case ::vr::k_eControllerAxis_Trigger:
+            if (j <= 2) {
+              ++numButtons;
+            } else {
+          #ifdef DEBUG
+              // SteamVR Knuckles is the only special case for using 2D axis values on triggers.
+              ::vr::ETrackedPropertyError err;
+              uint32_t requiredBufferLen;
+              char charBuf[128];
+              requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(trackedDevice,
+                                  ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
+              MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+              nsCString deviceId(charBuf);
+              MOZ_ASSERT(deviceId.Find("knuckles") != kNotFound);
+          #endif // #ifdef DEBUG
+              numButtons += 2;
+            }
+            break;
+        }
+      }
+
+      // Scan the buttons that the controllers support
+      const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
+                                       trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_A)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_Grip)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
+        ++numButtons;
+      }
+
+      nsCString deviceId;
+      GetControllerDeviceId(deviceType, trackedDevice, deviceId);
+
+      strncpy(controllerState.controllerName, deviceId.BeginReading(), kVRControllerNameMaxLen);
+      controllerState.numButtons = numButtons;
+      controllerState.numAxes = numAxes;
+      controllerState.numHaptics = kNumOpenVRHaptcs;
+
+      // If the Windows MR controller doesn't has the amount
+      // of buttons or axes as our expectation, switching off
+      // the workaround for Windows MR.
+      if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
+        mIsWindowsMR = false;
+        NS_WARNING("OpenVR - Switching off Windows MR mode.");
+      }
+    }
+    controllerPresent[stateIndex] = true;
   }
-
-  state.sensorState.CalcViewMatrices(headToEyeTransforms);
-  state.sensorState.inputFrameID++;
+  // Clear out entries for disconnected controllers
+  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    if (!controllerPresent[stateIndex] && mControllerDeviceIndex[stateIndex] != 0) {
+      mControllerDeviceIndex[stateIndex] = 0;
+      memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
+    }
+  }
 }
 
 void
-OpenVRSession::GetControllerState(VRSystemState &state)
+OpenVRSession::UpdateControllerButtons(VRSystemState& aState)
 {
-  // TODO - Implement
+  MOZ_ASSERT(mVRSystem);
+
+  // Compared to Edge, we have a wrong implementation for the vertical axis value.
+  // In order to not affect the current VR content, we add a workaround for yAxis.
+  const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
+  const float triggerThreshold = gfxPrefs::VRControllerTriggerThreshold();
+
+  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    ::vr::TrackedDeviceIndex_t trackedDevice = mControllerDeviceIndex[stateIndex];
+    if (trackedDevice == 0) {
+      continue;
+    }
+    VRControllerState& controllerState = aState.controllerState[stateIndex];
+    const ::vr::ETrackedControllerRole role = mVRSystem->
+                                          GetControllerRoleForTrackedDeviceIndex(
+                                          trackedDevice);
+    dom::GamepadHand hand = GetControllerHandFromControllerRole(role);
+    controllerState.hand = hand;
+
+    ::vr::VRControllerState_t vrControllerState;
+    if (mVRSystem->GetControllerState(trackedDevice, &vrControllerState, sizeof(vrControllerState))) {
+      uint32_t axisIdx = 0;
+      uint32_t buttonIdx = 0;
+      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
+        const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
+                                   trackedDevice,
+                                   static_cast<::vr::TrackedDeviceProperty>(
+                                   ::vr::Prop_Axis0Type_Int32 + j));
+        switch (axisType) {
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
+          {
+            if (mIsWindowsMR) {
+              // Adjust the input mapping for Windows MR which has
+              // different order.
+              axisIdx = (axisIdx == 0) ? 2 : 0;
+              buttonIdx = (buttonIdx == 0) ? 4 : 0;
+            }
+
+            controllerState.axisValue[axisIdx] = vrControllerState.rAxis[j].x;
+            ++axisIdx;
+            controllerState.axisValue[axisIdx] = vrControllerState.rAxis[j].y * yAxisInvert;
+            ++axisIdx;
+            uint64_t buttonMask = ::vr::ButtonMaskFromId(
+                                 static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j));
+
+            UpdateButton(controllerState, vrControllerState, buttonIdx, buttonMask);
+            ++buttonIdx;
+
+            if (mIsWindowsMR) {
+              axisIdx = (axisIdx == 4) ? 2 : 4;
+              buttonIdx = (buttonIdx == 5) ? 1 : 2;
+            }
+            break;
+          }
+          case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
+          {
+            if (j <= 2) {
+              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].x, triggerThreshold);
+              ++buttonIdx;
+            } else {
+              // For SteamVR Knuckles.
+              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].x, triggerThreshold);
+              ++buttonIdx;
+              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].y, triggerThreshold);
+              ++buttonIdx;
+            }
+            break;
+          }
+        }
+      }
+      MOZ_ASSERT(axisIdx ==
+                 controller->GetControllerInfo().GetNumAxes());
+
+      const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
+                                         trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_A)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_A));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_Grip)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_Grip));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_ApplicationMenu));
+        ++buttonIdx;
+      }
+      if (mIsWindowsMR) {
+        // button 4 in Windows MR has already been assigned
+        // to k_eControllerAxis_TrackPad.
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Left));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Up));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Right));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Down));
+        ++buttonIdx;
+      }
+    }
+  }
+}
+
+void
+OpenVRSession::UpdateControllerPoses(VRSystemState& aState)
+{
+  MOZ_ASSERT(mVRSystem);
+
+  // Compared to Edge, we have a wrong implementation for the vertical axis value.
+  // In order to not affect the current VR content, we add a workaround for yAxis.
+  const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
+  const float triggerThreshold = gfxPrefs::VRControllerTriggerThreshold();
+
+  ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
+  mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
+                                             poses, ::vr::k_unMaxTrackedDeviceCount);
+
+  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    ::vr::TrackedDeviceIndex_t trackedDevice = mControllerDeviceIndex[stateIndex];
+    if (trackedDevice == 0) {
+      continue;
+    }
+    VRControllerState& controllerState = aState.controllerState[stateIndex];
+    const ::vr::TrackedDevicePose_t& pose = poses[trackedDevice];
+
+    if (pose.bDeviceIsConnected) {
+      controllerState.flags = (dom::GamepadCapabilityFlags::Cap_Orientation |
+                               dom::GamepadCapabilityFlags::Cap_Position);
+    } else {
+      controllerState.flags =  dom::GamepadCapabilityFlags::Cap_None;
+    }
+    if (pose.bPoseIsValid &&
+        pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
+      gfx::Matrix4x4 m;
+
+      // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
+      // because of its arrangement, we can copy the 12 elements in and
+      // then transpose them to the right place.  We do this so we can
+      // pull out a Quaternion.
+      memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
+      m.Transpose();
+
+      gfx::Quaternion rot;
+      rot.SetFromRotationMatrix(m);
+      rot.Invert();
+
+      controllerState.pose.orientation[0] = rot.x;
+      controllerState.pose.orientation[1] = rot.y;
+      controllerState.pose.orientation[2] = rot.z;
+      controllerState.pose.orientation[3] = rot.w;
+      controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+      controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+      controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
+      controllerState.pose.angularAcceleration[0] = 0.0f;
+      controllerState.pose.angularAcceleration[1] = 0.0f;
+      controllerState.pose.angularAcceleration[2] = 0.0f;
+      controllerState.isOrientationValid = true;
+
+      controllerState.pose.position[0] = m._41;
+      controllerState.pose.position[1] = m._42;
+      controllerState.pose.position[2] = m._43;
+      controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
+      controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
+      controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
+      controllerState.pose.linearAcceleration[0] = 0.0f;
+      controllerState.pose.linearAcceleration[1] = 0.0f;
+      controllerState.pose.linearAcceleration[2] = 0.0f;
+      controllerState.isPositionValid = true;
+    } else {
+      controllerState.isOrientationValid = false;
+      controllerState.isPositionValid = false;
+    }
+  }
+}
+
+void
+OpenVRSession::GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
+                                     ::vr::TrackedDeviceIndex_t aDeviceIndex,
+                                     nsCString& aId)
+{
+  switch (aDeviceType) {
+    case ::vr::TrackedDeviceClass_Controller:
+    {
+      ::vr::ETrackedPropertyError err;
+      uint32_t requiredBufferLen;
+      bool isFound = false;
+      char charBuf[128];
+      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
+                          ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
+      if (requiredBufferLen > 128) {
+        MOZ_CRASH("Larger than the buffer size.");
+      }
+      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+      nsCString deviceId(charBuf);
+      if (deviceId.Find("knuckles") != kNotFound) {
+        aId.AssignLiteral("OpenVR Knuckles");
+        isFound = true;
+      }
+      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
+        ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
+      if (requiredBufferLen > 128) {
+        MOZ_CRASH("Larger than the buffer size.");
+      }
+      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+      deviceId.Assign(charBuf);
+      if (deviceId.Find("MRSOURCE") != kNotFound) {
+        aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
+        mIsWindowsMR = true;
+        isFound = true;
+      }
+      if (!isFound) {
+        aId.AssignLiteral("OpenVR Gamepad");
+      }
+      break;
+    }
+    case ::vr::TrackedDeviceClass_GenericTracker:
+    {
+      aId.AssignLiteral("OpenVR Tracker");
+      break;
+    }
+    default:
+      MOZ_ASSERT(false);
+      break;
+  }
 }
 
 void
 OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState)
 {
-  GetSensorState(aSystemState);
-  GetControllerState(aSystemState);
+  UpdateHeadsetPose(aSystemState);
+  UpdateEyeParameters(aSystemState);
+  EnumerateControllers(aSystemState);
+  UpdateControllerButtons(aSystemState);
+  UpdateControllerPoses(aSystemState);
+  aSystemState.sensorState.inputFrameID++;
 }
 
 bool
 OpenVRSession::ShouldQuit() const
 {
   return mShouldQuit;
 }
 
--- a/gfx/vr/service/OpenVRSession.h
+++ b/gfx/vr/service/OpenVRSession.h
@@ -37,29 +37,36 @@ public:
   void StopPresentation() override;
   bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override;
 
 private:
   // OpenVR State
   ::vr::IVRSystem* mVRSystem = nullptr;
   ::vr::IVRChaperone* mVRChaperone = nullptr;
   ::vr::IVRCompositor* mVRCompositor = nullptr;
+  ::vr::TrackedDeviceIndex_t mControllerDeviceIndex[kVRControllerMaxCount];
   bool mShouldQuit;
+  bool mIsWindowsMR;
 
   bool InitState(mozilla::gfx::VRSystemState& aSystemState);
-  void UpdateStageParameters(mozilla::gfx::VRDisplayState& state);
-  void UpdateEyeParameters(mozilla::gfx::VRDisplayState& state, gfx::Matrix4x4* headToEyeTransforms = nullptr);
-  void GetSensorState(mozilla::gfx::VRSystemState& state);
-  void GetControllerState(VRSystemState &state);
+  void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
+  void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
+  void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
+  void EnumerateControllers(VRSystemState& aState);
+  void UpdateControllerPoses(VRSystemState& aState);
+  void UpdateControllerButtons(VRSystemState& aState);
 
   bool SubmitFrame(void* aTextureHandle,
                    ::vr::ETextureType aTextureType,
                    const VRLayerEyeRect& aLeftEyeRect,
                    const VRLayerEyeRect& aRightEyeRect);
 #if defined(XP_WIN)
   bool CreateD3DObjects();
 #endif
+  void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
+                             ::vr::TrackedDeviceIndex_t aDeviceIndex,
+                             nsCString& aId);
 };
 
 } // namespace mozilla
 } // namespace gfx
 
 #endif // GFX_VR_SERVICE_OPENVRSESSION_H