Bug 1470527 - Implement Controller support for gfxVRExternal and VRServiceOpenVR draft
authorKearwood "Kip" Gilbert <kgilbert@mozilla.com>
Wed, 04 Jul 2018 16:09:37 -0700
changeset 816232 07999088a555c5e9d7c0948d1e67917fe52dc67d
parent 816164 9302fd8c95c05e5a5cd295dde3bbdac2d58d6256
push id115783
push userbmo:kgilbert@mozilla.com
push dateTue, 10 Jul 2018 19:23:30 +0000
bugs1470527
milestone63.0a1
Bug 1470527 - Implement Controller support for gfxVRExternal and VRServiceOpenVR MozReview-Commit-ID: Jw59Wkkv0NH
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,150 @@ VRDisplayClient::FireEvents()
     }
   }
 
   // Check if we need to trigger VRDisplay.requestAnimationFrame
   if (mLastEventFrameId != mDisplayInfo.mFrameId) {
     mLastEventFrameId = mDisplayInfo.mFrameId;
     vm->RunFrameRequestCallbacks();
   }
+
+  FireGamepadEvents();
+}
+
+void
+VRDisplayClient::FireGamepadEvents()
+{
+  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;
 
 struct Point3D_POD
 {
   float x;
   float y;
   float z;
@@ -71,16 +72,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,
@@ -129,30 +156,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 {
   int64_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 {
@@ -237,32 +269,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,56 +47,52 @@ 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;
-
 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;
 }
 
@@ -216,52 +212,32 @@ VRDisplayExternal::SubmitFrame(const lay
 
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
   manager->PushState(&state);
 
   VRDisplayState displayState;
   memset(&displayState, 0, sizeof(VRDisplayState));
   while (displayState.mLastSubmittedFrameId < aFrameId) {
-    if (manager->PullState(&displayState, &mLastSensorState)) {
+    if (manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState)) {
       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)
  , mSameProcess(aAPIShmem != nullptr)
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
 #elif defined(XP_WIN)
   mShmemFile = NULL;
@@ -423,17 +399,16 @@ VRSystemManagerExternal::Destroy()
 }
 
 void
 VRSystemManagerExternal::Shutdown()
 {
   if (mDisplay) {
     mDisplay = nullptr;
   }
-  RemoveControllers();
   CloseShmem();
 #if defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
 #endif
 }
 
 void
 VRSystemManagerExternal::NotifyVSync()
@@ -503,22 +478,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
 }
@@ -527,63 +496,79 @@ 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
+  return;
 }
 
 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);
+        }
         pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
         mDoShutdown = aDisplayState->shutdown;
         success = mExternalShmem->state.enumerationCompleted;
     }
 #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
@@ -40,41 +40,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,
                             void** 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;
@@ -90,27 +80,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);
 
 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
@@ -107,16 +107,19 @@ struct ParamTraits<mozilla::gfx::VRDispl
     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++) {
       WriteParam(aMsg, aParam.mLastSensorState[i]);
     }
+    for (int i = 0; i < mozilla::gfx::kVRControllerMaxCount; 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)) ||
@@ -124,31 +127,33 @@ struct ParamTraits<mozilla::gfx::VRDispl
         !ReadParam(aMsg, aIter, &(aResult->mDisplayState))) {
       return false;
     }
     for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mLastSensorState[i]))) {
         return false;
       }
     }
-
+    for (int i = 0; i < mozilla::gfx::kVRControllerMaxCount; 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 +163,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,16 +188,47 @@ 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;
     }
+    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 (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->pose))) {
+      return false;
+    }
     for (int i=0; i < 16; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->leftViewMatrix[i]))) {
         return false;
       }
     }
     for (int i=0; i < 16; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->rightViewMatrix[i]))) {
         return false;
@@ -241,55 +268,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);
+    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 (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
-      WriteParam(aMsg, aParam.mAxisValue[i]);
+      WriteParam(aMsg, aParam.axisValue[i]);
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
-      WriteParam(aMsg, aParam.mTriggerValue[i]);
+    for (int i=0; i < mozilla::gfx::kVRControllerMaxButtons; 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]))) {
+      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 (int i=0; i < mozilla::gfx::kVRControllerMaxButtons; 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,421 @@ 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;
+          }
+        }
+      }
+
+      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);
+
+  ::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