Bug 1436791 - Implement gfxVRExternal draft
authorKearwood Gilbert <kearwood@kearwood.com>
Thu, 08 Feb 2018 16:15:16 -0800
changeset 766572 093e9711557cc70e6dfe283dc7d30e1c3d0733b9
parent 764770 a6a32fb286fa9e5d5f6d5b3b77423ab6b96c9502
child 767009 4d40011d64f00c984be51325ad569f6323fa0e77
push id102358
push userbmo:kgilbert@mozilla.com
push dateTue, 13 Mar 2018 01:09:55 +0000
bugs1436791
milestone60.0a1
Bug 1436791 - Implement gfxVRExternal - gfxVRExternal enables other processes to present real or simulated VR hardware to Firefox. - This functionality is disabled by default, under dom.vr.external.enabled. - VRDisplayInfo, VRControllerInfo, and associated structs have been restructured to ensure internal state is not exposed via shmem interface. - Some refactoring to convert structs to POD types, enabling them to be located in shmem and be memcpy'd. - Work needed before unpreffing marked with "TODO" comments. MozReview-Commit-ID: FbsusbxuoQ8
dom/vr/VRDisplay.cpp
dom/vr/VRServiceTest.cpp
dom/vr/VRServiceTest.h
gfx/thebes/gfxPrefs.h
gfx/vr/VRDisplayClient.cpp
gfx/vr/VRDisplayHost.cpp
gfx/vr/VRDisplayHost.h
gfx/vr/VRManager.cpp
gfx/vr/VRManager.h
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/gfxVRGVR.cpp
gfx/vr/gfxVROSVR.cpp
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVROpenVR.h
gfx/vr/gfxVRPuppet.cpp
gfx/vr/gfxVRPuppet.h
gfx/vr/ipc/VRMessageUtils.h
gfx/vr/moz.build
modules/libpref/init/all.js
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -388,17 +388,17 @@ VRDisplay::LastRelease()
   // We don't want to wait for the CC to free up the presentation
   // for use in other documents, so we do this in LastRelease().
   Shutdown();
 }
 
 already_AddRefed<VREyeParameters>
 VRDisplay::GetEyeParameters(VREye aEye)
 {
-  gfx::VRDisplayInfo::Eye eye = aEye == VREye::Left ? gfx::VRDisplayInfo::Eye_Left : gfx::VRDisplayInfo::Eye_Right;
+  gfx::VRDisplayState::Eye eye = aEye == VREye::Left ? gfx::VRDisplayState::Eye_Left : gfx::VRDisplayState::Eye_Right;
   RefPtr<VREyeParameters> params =
     new VREyeParameters(GetParentObject(),
                         mClient->GetDisplayInfo().GetEyeTranslation(eye),
                         mClient->GetDisplayInfo().GetEyeFOV(eye),
                         mClient->GetDisplayInfo().SuggestedEyeResolution());
   return params.forget();
 }
 
@@ -909,19 +909,19 @@ VRFrameInfo::Update(const gfx::VRDisplay
   mVRState.timestamp = aState.timestamp + mTimeStampOffset;
 
   // Avoid division by zero within ConstructProjectionMatrix
   const float kEpsilon = 0.00001f;
   if (fabs(aDepthFar - aDepthNear) < kEpsilon) {
     aDepthFar = aDepthNear + kEpsilon;
   }
 
-  const gfx::VRFieldOfView leftFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Left];
+  const gfx::VRFieldOfView leftFOV = aInfo.mDisplayState.mEyeFOV[gfx::VRDisplayState::Eye_Left];
   mLeftProjection = leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
-  const gfx::VRFieldOfView rightFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Right];
+  const gfx::VRFieldOfView rightFOV = aInfo.mDisplayState.mEyeFOV[gfx::VRDisplayState::Eye_Right];
   mRightProjection = rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
   memcpy(mLeftView.components, aState.leftViewMatrix, sizeof(aState.leftViewMatrix));
   memcpy(mRightView.components, aState.rightViewMatrix, sizeof(aState.rightViewMatrix));
 }
 
 VRFrameInfo::VRFrameInfo()
  : mTimeStampOffset(0.0f)
 {
--- a/dom/vr/VRServiceTest.cpp
+++ b/dom/vr/VRServiceTest.cpp
@@ -27,54 +27,55 @@ NS_IMPL_ADDREF_INHERITED(VRMockDisplay, 
 NS_IMPL_RELEASE_INHERITED(VRMockDisplay, DOMEventTargetHelper)
 
 VRMockDisplay::VRMockDisplay(const nsCString& aID, uint32_t aDeviceID)
  : mDeviceID(aDeviceID)
  , mDisplayInfo{}
  , mSensorState{}
  , mTimestamp(TimeStamp::Now())
 {
-  mDisplayInfo.mDisplayName = aID;
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
+  strncpy(state.mDisplayName, aID.BeginReading(), kVRDisplayNameMaxLen);
   mDisplayInfo.mType = VRDeviceType::Puppet;
-  mDisplayInfo.mIsConnected = true;
-  mDisplayInfo.mIsMounted = false;
-  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
-                                  VRDisplayCapabilityFlags::Cap_Orientation |
-                                  VRDisplayCapabilityFlags::Cap_AngularAcceleration |
-                                  VRDisplayCapabilityFlags::Cap_Position |
-                                  VRDisplayCapabilityFlags::Cap_LinearAcceleration |
-                                  VRDisplayCapabilityFlags::Cap_External |
-                                  VRDisplayCapabilityFlags::Cap_Present |
-                                  VRDisplayCapabilityFlags::Cap_StageParameters |
-                                  VRDisplayCapabilityFlags::Cap_MountDetection;
+  state.mIsConnected = true;
+  state.mIsMounted = false;
+  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
+                           VRDisplayCapabilityFlags::Cap_Orientation |
+                           VRDisplayCapabilityFlags::Cap_AngularAcceleration |
+                           VRDisplayCapabilityFlags::Cap_Position |
+                           VRDisplayCapabilityFlags::Cap_LinearAcceleration |
+                           VRDisplayCapabilityFlags::Cap_External |
+                           VRDisplayCapabilityFlags::Cap_Present |
+                           VRDisplayCapabilityFlags::Cap_StageParameters |
+                           VRDisplayCapabilityFlags::Cap_MountDetection;
 }
 
 JSObject*
 VRMockDisplay::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VRMockDisplayBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void VRMockDisplay::SetEyeResolution(unsigned long aRenderWidth, unsigned long aRenderHeight)
 {
-  mDisplayInfo.mEyeResolution.width = aRenderWidth;
-  mDisplayInfo.mEyeResolution.height = aRenderHeight;
+  mDisplayInfo.mDisplayState.mEyeResolution.width = aRenderWidth;
+  mDisplayInfo.mDisplayState.mEyeResolution.height = aRenderHeight;
 }
 
 void
 VRMockDisplay::SetEyeParameter(VREye aEye, double aOffsetX, double aOffsetY,
                                double aOffsetZ, double aUpDegree, double aRightDegree,
                                double aDownDegree, double aLeftDegree)
 {
   uint32_t eye = static_cast<uint32_t>(aEye);
-  mDisplayInfo.mEyeFOV[eye] = gfx ::VRFieldOfView(aUpDegree, aRightDegree,
-                                                  aRightDegree, aLeftDegree);
-  mDisplayInfo.mEyeTranslation[eye].x = aOffsetX;
-  mDisplayInfo.mEyeTranslation[eye].y = aOffsetY;
-  mDisplayInfo.mEyeTranslation[eye].z = aOffsetZ;
+  mDisplayInfo.mDisplayState.mEyeFOV[eye] = gfx ::VRFieldOfView(aUpDegree, aRightDegree,
+                                                                aRightDegree, aLeftDegree);
+  mDisplayInfo.mDisplayState.mEyeTranslation[eye].x = aOffsetX;
+  mDisplayInfo.mDisplayState.mEyeTranslation[eye].y = aOffsetY;
+  mDisplayInfo.mDisplayState.mEyeTranslation[eye].z = aOffsetZ;
 }
 
 void
 VRMockDisplay::SetPose(const Nullable<Float32Array>& aPosition,
                        const Nullable<Float32Array>& aLinearVelocity,
                        const Nullable<Float32Array>& aLinearAcceleration,
                        const Nullable<Float32Array>& aOrientation,
                        const Nullable<Float32Array>& aAngularVelocity,
--- a/dom/vr/VRServiceTest.h
+++ b/dom/vr/VRServiceTest.h
@@ -24,17 +24,17 @@ public:
   VRMockDisplay(const nsCString& aID, uint32_t aDeviceID);
   void SetEyeParameter(VREye aEye, double aOffsetX, double aOffsetY, double aOffsetZ,
                        double aUpDegree, double aRightDegree,
                        double aDownDegree, double aLeftDegree);
   void SetEyeResolution(unsigned long aRenderWidth, unsigned long aRenderHeight);
   void SetPose(const Nullable<Float32Array>& aPosition, const Nullable<Float32Array>& aLinearVelocity,
                const Nullable<Float32Array>& aLinearAcceleration, const Nullable<Float32Array>& aOrientation,
                const Nullable<Float32Array>& aAngularVelocity, const Nullable<Float32Array>& aAngularAcceleration);
-  void SetMountState(bool aIsMounted) { mDisplayInfo.mIsMounted = aIsMounted; }
+  void SetMountState(bool aIsMounted) { mDisplayInfo.mDisplayState.mIsMounted = aIsMounted; }
   void Update();
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
   ~VRMockDisplay() = default;
 
   uint32_t mDeviceID;
   gfx::VRDisplayInfo mDisplayInfo;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -360,16 +360,17 @@ private:
   DECL_GFX_PREF(Live, "browser.ui.zoom.force-user-scalable",   ForceUserScalable, bool, false);
   DECL_GFX_PREF(Live, "browser.viewport.desktopWidth",         DesktopViewportWidth, int32_t, 980);
 
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled",           VRAutoActivateEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold",   VRControllerTriggerThreshold, float, 0.1f);
+  DECL_GFX_PREF(Once, "dom.vr.external.enabled",               VRExternalEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.navigation.timeout",             VRNavigationTimeout, int32_t, 1000);
   DECL_GFX_PREF(Once, "dom.vr.oculus.enabled",                 VROculusEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.oculus.invisible.enabled",       VROculusInvisibleEnabled, bool, true);
   DECL_GFX_PREF(Live, "dom.vr.oculus.present.timeout",         VROculusPresentTimeout, int32_t, 500);
   DECL_GFX_PREF(Live, "dom.vr.oculus.quit.timeout",            VROculusQuitTimeout, int32_t, 10000);
   DECL_GFX_PREF(Once, "dom.vr.openvr.enabled",                 VROpenVREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.osvr.enabled",                   VROSVREnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.vr.controller.enumerate.interval",  VRControllerEnumerateInterval, int32_t, 1000);
--- a/gfx/vr/VRDisplayClient.cpp
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -103,25 +103,25 @@ VRDisplayClient::FireEvents()
 
   // Check if we need to trigger onVRDisplayPresentChange event
   if (bLastEventWasPresenting != isPresenting) {
     bLastEventWasPresenting = isPresenting;
     vm->FireDOMVRDisplayPresentChangeEvent(mDisplayInfo.mDisplayID);
   }
 
   // Check if we need to trigger onvrdisplayactivate event
-  if (!bLastEventWasMounted && mDisplayInfo.mIsMounted) {
+  if (!bLastEventWasMounted && mDisplayInfo.mDisplayState.mIsMounted) {
     bLastEventWasMounted = true;
     if (gfxPrefs::VRAutoActivateEnabled()) {
       vm->FireDOMVRDisplayMountedEvent(mDisplayInfo.mDisplayID);
     }
   }
 
   // Check if we need to trigger onvrdisplaydeactivate event
-  if (bLastEventWasMounted && !mDisplayInfo.mIsMounted) {
+  if (bLastEventWasMounted && !mDisplayInfo.mDisplayState.mIsMounted) {
     bLastEventWasMounted = false;
     if (gfxPrefs::VRAutoActivateEnabled()) {
       vm->FireDOMVRDisplayUnmountedEvent(mDisplayInfo.mDisplayID);
     }
   }
 
   // Check if we need to trigger VRDisplay.requestAnimationFrame
   if (mLastEventFrameId != mDisplayInfo.mFrameId) {
@@ -140,17 +140,17 @@ bool
 VRDisplayClient::GetIsConnected() const
 {
   return mDisplayInfo.GetIsConnected();
 }
 
 void
 VRDisplayClient::NotifyDisconnected()
 {
-  mDisplayInfo.mIsConnected = false;
+  mDisplayInfo.mDisplayState.mIsConnected = false;
 }
 
 void
 VRDisplayClient::UpdateSubmitFrameResult(const VRSubmitFrameResultInfo& aResult)
 {
   mSubmitFrameResult = aResult;
 }
 
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -73,16 +73,17 @@ VRDisplayHost::VRDisplayHost(VRDeviceTyp
 {
   MOZ_COUNT_CTOR(VRDisplayHost);
   mDisplayInfo.mType = aType;
   mDisplayInfo.mDisplayID = VRSystemManager::AllocateDisplayID();
   mDisplayInfo.mPresentingGroups = 0;
   mDisplayInfo.mGroupMask = kVRGroupContent;
   mDisplayInfo.mFrameId = 0;
   mDisplayInfo.mPresentingGeneration = 0;
+  mDisplayInfo.mDisplayState.mDisplayName[0] = '\0';
 }
 
 VRDisplayHost::~VRDisplayHost()
 {
   if (mSubmitThread) {
     mSubmitThread->Shutdown();
     mSubmitThread = nullptr;
   }
@@ -155,17 +156,17 @@ void
 VRDisplayHost::SetGroupMask(uint32_t aGroupMask)
 {
   mDisplayInfo.mGroupMask = aGroupMask;
 }
 
 bool
 VRDisplayHost::GetIsConnected()
 {
-  return mDisplayInfo.mIsConnected;
+  return mDisplayInfo.mDisplayState.mIsConnected;
 }
 
 void
 VRDisplayHost::AddLayer(VRLayerParent *aLayer)
 {
   mLayers.AppendElement(aLayer);
   mDisplayInfo.mPresentingGroups |= aLayer->GetGroup();
   if (mLayers.Length() == 1) {
@@ -416,23 +417,22 @@ VRDisplayHost::CheckClearDisplayInfoDirt
     return false;
   }
   mLastUpdateDisplayInfo = mDisplayInfo;
   return true;
 }
 
 VRControllerHost::VRControllerHost(VRDeviceType aType, dom::GamepadHand aHand,
                                    uint32_t aDisplayID)
- : mButtonPressed(0)
- , mButtonTouched(0)
+ : mControllerInfo{}
  , mVibrateIndex(0)
 {
   MOZ_COUNT_CTOR(VRControllerHost);
   mControllerInfo.mType = aType;
-  mControllerInfo.mHand = aHand;
+  mControllerInfo.mControllerState.mHand = aHand;
   mControllerInfo.mMappingType = dom::GamepadMappingType::_empty;
   mControllerInfo.mDisplayID = aDisplayID;
   mControllerInfo.mControllerID = VRSystemManager::AllocateControllerID();
 }
 
 VRControllerHost::~VRControllerHost()
 {
   MOZ_COUNT_DTOR(VRControllerHost);
@@ -442,35 +442,35 @@ const VRControllerInfo&
 VRControllerHost::GetControllerInfo() const
 {
   return mControllerInfo;
 }
 
 void
 VRControllerHost::SetButtonPressed(uint64_t aBit)
 {
-  mButtonPressed = aBit;
+  mControllerInfo.mControllerState.mButtonPressed = aBit;
 }
 
 uint64_t
 VRControllerHost::GetButtonPressed()
 {
-  return mButtonPressed;
+  return mControllerInfo.mControllerState.mButtonPressed;
 }
 
 void
 VRControllerHost::SetButtonTouched(uint64_t aBit)
 {
-  mButtonTouched = aBit;
+  mControllerInfo.mControllerState.mButtonTouched = aBit;
 }
 
 uint64_t
 VRControllerHost::GetButtonTouched()
 {
-  return mButtonTouched;
+  return mControllerInfo.mControllerState.mButtonTouched;
 }
 
 void
 VRControllerHost::SetPose(const dom::GamepadPoseState& aPose)
 {
   mPose = aPose;
 }
 
@@ -478,17 +478,17 @@ const dom::GamepadPoseState&
 VRControllerHost::GetPose()
 {
   return mPose;
 }
 
 dom::GamepadHand
 VRControllerHost::GetHand()
 {
-  return mControllerInfo.mHand;
+  return mControllerInfo.mControllerState.mHand;
 }
 
 void
 VRControllerHost::SetVibrateIndex(uint64_t aIndex)
 {
   mVibrateIndex = aIndex;
 }
 
--- a/gfx/vr/VRDisplayHost.h
+++ b/gfx/vr/VRDisplayHost.h
@@ -142,20 +142,16 @@ public:
   uint64_t GetVibrateIndex();
 
 protected:
   explicit VRControllerHost(VRDeviceType aType, dom::GamepadHand aHand,
                             uint32_t aDisplayID);
   virtual ~VRControllerHost();
 
   VRControllerInfo mControllerInfo;
-  // The current button pressed bit of button mask.
-  uint64_t mButtonPressed;
-  // The current button touched bit of button mask.
-  uint64_t mButtonTouched;
   uint64_t mVibrateIndex;
   dom::GamepadPoseState mPose;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_DISPLAY_HOST_H */
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/dom/VRDisplay.h"
 #include "mozilla/dom/GamepadEventTypes.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/Unused.h"
 
 #include "gfxPrefs.h"
 #include "gfxVR.h"
+#include "gfxVRExternal.h"
 #if defined(XP_WIN)
 #include "gfxVROculus.h"
 #endif
 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
 #include "gfxVROpenVR.h"
 #include "gfxVROSVR.h"
 #endif
 #if defined(MOZ_ANDROID_GOOGLE_VR)
@@ -71,16 +72,20 @@ VRManager::VRManager()
    * native interface for Oculus HMD's.
    *
    * OpenvR comes second, as it is the native interface for HTC Vive
    * which is the most common HMD at this time.
    *
    * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
    * to support everyone else.
    */
+  mExternalManager = VRSystemManagerExternal::Create();
+  if (mExternalManager) {
+      mManagers.AppendElement(mExternalManager);
+  }
 
 #if defined(XP_WIN)
   // The Oculus runtime is supported only on Windows
   mgr = VRSystemManagerOculus::Create();
   if (mgr) {
     mManagers.AppendElement(mgr);
   }
 #endif
@@ -508,16 +513,23 @@ VRManager::CreateVRTestSystem()
 
 VRSystemManagerPuppet*
 VRManager::GetPuppetManager()
 {
   MOZ_ASSERT(mPuppetManager);
   return mPuppetManager;
 }
 
+VRSystemManagerExternal*
+VRManager::GetExternalManager()
+{
+  MOZ_ASSERT(mExternalManager);
+  return mExternalManager;
+}
+
 template<class T>
 void
 VRManager::NotifyGamepadChange(uint32_t aIndex, const T& aInfo)
 {
   dom::GamepadChangeEventBody body(aInfo);
   dom::GamepadChangeEvent e(aIndex, dom::GamepadServiceType::VR, body);
 
   for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -19,16 +19,17 @@ namespace layers {
 class TextureHost;
 }
 namespace gfx {
 
 class VRLayerParent;
 class VRManagerParent;
 class VRDisplayHost;
 class VRSystemManagerPuppet;
+class VRSystemManagerExternal;
 
 class VRManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRManager)
 
 public:
   static void ManagerInit();
   static VRManager* Get();
@@ -44,16 +45,17 @@ public:
   void RemoveControllers();
   template<class T> void NotifyGamepadChange(uint32_t aIndex, const T& aInfo);
   RefPtr<gfx::VRDisplayHost> GetDisplay(const uint32_t& aDisplayID);
   void GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo);
   RefPtr<gfx::VRControllerHost> GetController(const uint32_t& aControllerID);
   void GetVRControllerInfo(nsTArray<VRControllerInfo>& aControllerInfo);
   void CreateVRTestSystem();
   VRSystemManagerPuppet* GetPuppetManager();
+  VRSystemManagerExternal* GetExternalManager();
 
   void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
                      double aIntensity, double aDuration, const VRManagerPromise& aPromise);
   void StopVibrateHaptic(uint32_t aControllerIdx);
   void NotifyVibrateHapticCompleted(const VRManagerPromise& aPromise);
   void DispatchSubmitFrameResult(uint32_t aDisplayID, const VRSubmitFrameResultInfo& aResult);
 
 protected:
@@ -84,16 +86,17 @@ private:
   VRControllerHostHashMap mVRControllers;
 
   Atomic<bool> mInitialized;
 
   TimeStamp mLastControllerEnumerationTime;
   TimeStamp mLastDisplayEnumerationTime;
   TimeStamp mLastActiveTime;
   RefPtr<VRSystemManagerPuppet> mPuppetManager;
+  RefPtr<VRSystemManagerExternal> mExternalManager;
   bool mVRDisplaysRequested;
   bool mVRControllersRequested;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // GFX_VR_MANAGER_H
new file mode 100644
--- /dev/null
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_EXTERNAL_API_H
+#define GFX_VR_EXTERNAL_API_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <type_traits>
+
+#ifdef MOZILLA_INTERNAL_API
+#include "mozilla/TypedEnumBits.h"
+#include "mozilla/gfx/2D.h"
+#endif // MOZILLA_INTERNAL_API
+
+namespace mozilla {
+#ifdef MOZILLA_INTERNAL_API
+namespace dom {
+  enum class GamepadHand : uint8_t;
+}
+#endif //  MOZILLA_INTERNAL_API
+namespace gfx {
+
+// 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 kVRControllerMaxAxis = 16;
+static const int kVRLayerMaxCount = 8;
+
+struct Point3D_POD
+{
+  float x;
+  float y;
+  float z;
+};
+
+struct IntSize_POD
+{
+  int32_t width;
+  int32_t height;
+};
+
+struct FloatSize_POD
+{
+  float width;
+  float height;
+};
+
+#ifndef MOZILLA_INTERNAL_API
+
+enum class ControllerHand : uint8_t {
+  _empty,
+  Left,
+  Right,
+  EndGuard_
+};
+
+#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,
+  /**
+    * Cap_Orientation is set if the VRDisplay is capable of tracking its orientation.
+    */
+  Cap_Orientation = 1 << 2,
+  /**
+   * Cap_Present is set if the VRDisplay is capable of presenting content to an
+   * HMD or similar device.  Can be used to indicate "magic window" devices that
+   * are capable of 6DoF tracking but for which requestPresent is not meaningful.
+   * If false then calls to requestPresent should always fail, and
+   * getEyeParameters should return null.
+   */
+  Cap_Present = 1 << 3,
+  /**
+   * Cap_External is set if the VRDisplay is separate from the device's
+   * primary display. If presenting VR content will obscure
+   * other content on the device, this should be un-set. When
+   * un-set, the application should not attempt to mirror VR content
+   * or update non-VR UI because that content will not be visible.
+   */
+  Cap_External = 1 << 4,
+  /**
+   * Cap_AngularAcceleration is set if the VRDisplay is capable of tracking its
+   * angular acceleration.
+   */
+  Cap_AngularAcceleration = 1 << 5,
+  /**
+   * Cap_LinearAcceleration is set if the VRDisplay is capable of tracking its
+   * linear acceleration.
+   */
+  Cap_LinearAcceleration = 1 << 6,
+  /**
+   * Cap_StageParameters is set if the VRDisplay is capable of room scale VR
+   * and can report the StageParameters to describe the space.
+   */
+  Cap_StageParameters = 1 << 7,
+  /**
+   * Cap_MountDetection is set if the VRDisplay is capable of sensing when the
+   * user is wearing the device.
+   */
+  Cap_MountDetection = 1 << 8,
+  /**
+   * Cap_All used for validity checking during IPC serialization
+   */
+  Cap_All = (1 << 9) - 1
+};
+
+#ifdef MOZILLA_INTERNAL_API
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
+#endif // MOZILLA_INTERNAL_API
+
+struct VRHMDSensorState {
+  int64_t inputFrameID;
+  double timestamp;
+  VRDisplayCapabilityFlags flags;
+
+  // These members will only change with inputFrameID:
+  float orientation[4];
+  float position[3];
+  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 {
+    return inputFrameID == other.inputFrameID &&
+           timestamp == other.timestamp;
+  }
+
+  bool operator!=(const VRHMDSensorState& other) const {
+    return !(*this == other);
+  }
+
+  void CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms);
+
+#endif // MOZILLA_INTERNAL_API
+};
+
+struct VRFieldOfView {
+  double upDegrees;
+  double rightDegrees;
+  double downDegrees;
+  double leftDegrees;
+
+#ifdef MOZILLA_INTERNAL_API
+
+  VRFieldOfView() = default;
+  VRFieldOfView(double up, double right, double down, double left)
+    : upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
+  {}
+
+  void SetFromTanRadians(double up, double right, double down, double left)
+  {
+    upDegrees = atan(up) * 180.0 / M_PI;
+    rightDegrees = atan(right) * 180.0 / M_PI;
+    downDegrees = atan(down) * 180.0 / M_PI;
+    leftDegrees = atan(left) * 180.0 / M_PI;
+  }
+
+  bool operator==(const VRFieldOfView& other) const {
+    return other.upDegrees == upDegrees &&
+           other.downDegrees == downDegrees &&
+           other.rightDegrees == rightDegrees &&
+           other.leftDegrees == leftDegrees;
+  }
+
+  bool operator!=(const VRFieldOfView& other) const {
+    return !(*this == other);
+  }
+
+  bool IsZero() const {
+    return upDegrees == 0.0 ||
+      rightDegrees == 0.0 ||
+      downDegrees == 0.0 ||
+      leftDegrees == 0.0;
+  }
+
+  Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar, bool rightHanded) const;
+
+#endif // MOZILLA_INTERNAL_API
+
+};
+
+struct VRDisplayState
+{
+  enum Eye {
+    Eye_Left,
+    Eye_Right,
+    NumEyes
+  };
+
+  char mDisplayName[kVRDisplayNameMaxLen];
+  VRDisplayCapabilityFlags mCapabilityFlags;
+  VRFieldOfView mEyeFOV[VRDisplayState::NumEyes];
+  Point3D_POD mEyeTranslation[VRDisplayState::NumEyes];
+  IntSize_POD mEyeResolution;
+  bool mIsConnected;
+  bool mIsMounted;
+  FloatSize_POD mStageSize;
+  // We can't use a Matrix4x4 here unless we ensure it's a POD type
+  float mSittingToStandingTransform[16];
+};
+
+struct VRControllerState
+{
+  char mControllerName[kVRControllerNameMaxLen];
+#ifdef MOZILLA_INTERNAL_API
+  dom::GamepadHand mHand;
+#else
+  ControllerHand mHand;
+#endif
+  uint32_t mNumButtons;
+  uint32_t mNumAxes;
+  uint32_t mNumTriggers;
+  uint32_t mNumHaptics;
+  // The current button pressed bit of button mask.
+  uint64_t mButtonPressed;
+  // The current button touched bit of button mask.
+  uint64_t mButtonTouched;
+  float mTriggerValue[kVRControllerMaxTriggers];
+  float mAxisValue[kVRControllerMaxAxis];
+};
+
+struct VRLayerEyeRect
+{
+  float x;
+  float y;
+  float width;
+  float height;
+};
+
+enum class VRLayerType : uint16_t {
+  LayerType_None = 0,
+  LayerType_2D_Content = 1,
+  LayerType_Stereo_Immersive = 2
+};
+
+enum class VRLayerTextureType : uint16_t {
+  LayerTextureType_None = 0,
+  LayerTextureType_DirectX = 1,
+  LayerTextureType_OpenGL = 2,
+  LayerTextureType_Vulkan = 3
+};
+
+struct VRLayer_2D_Content
+{
+  void* mTextureHandle;
+  VRLayerTextureType mTextureType;
+  uint64_t mFrameId;
+};
+
+struct VRLayer_Stereo_Immersive
+{
+  void* mTextureHandle;
+  VRLayerTextureType mTextureType;
+  uint64_t mFrameId;
+  VRLayerEyeRect mLeftEyeRect;
+  VRLayerEyeRect mRightEyeRect;
+};
+
+struct VRLayerState
+{
+  VRLayerType type;
+  union {
+    VRLayer_2D_Content layer_2d_content;
+    VRLayer_Stereo_Immersive layer_stereo_immersive;
+  };
+};
+
+struct VRBrowserState
+{
+  VRLayerState layerState[kVRLayerMaxCount];
+};
+
+struct VRSystemState
+{
+  VRDisplayState displayState;
+  VRHMDSensorState sensorState;
+  VRControllerState controllerState[kVRControllerMaxCount];
+};
+
+struct VRExternalShmem
+{
+  int64_t generationA;
+  VRSystemState state;
+  int64_t generationB;
+  int64_t browserGenerationA;
+  VRBrowserState browserState;
+  int64_t browserGenerationB;
+};
+
+// As we are memcpy'ing VRExternalShmem and its members around, it must be a POD type
+static_assert(std::is_pod<VRExternalShmem>::value, "VRExternalShmem must be a POD type.");
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_EXTERNAL_API_H */
--- a/gfx/vr/gfxVR.cpp
+++ b/gfx/vr/gfxVR.cpp
@@ -38,21 +38,21 @@ VRSystemManager::AllocateControllerID()
  * not active, in order to poll for respond to VR Platform API requests.
  * This should be called very often, ideally once per frame.
  * VRSystemManager::Refresh will not activate VR hardware or
  * initialize VR runtimes that have not already been activated.
  */
 void
 VRSystemManager::NotifyVSync()
 {
-  // VRDisplayHost::NotifyVSync may modify mVRDisplays, so we iterate
-  // through a local copy here.
-  nsTArray<RefPtr<VRDisplayHost>> displays;
+  // VRDisplayHost::NotifyVSync may modify mVRDisplays, so we iterate
+  // through a local copy here.
+  nsTArray<RefPtr<VRDisplayHost>> displays;
   GetHMDs(displays);
-  for (const auto& display : displays) {
+  for (const auto& display : displays) {
     display->NotifyVSync();
   }
 
   // Ensure that the controller state is updated at least
   // on every 2d display VSync when not in a VR presentation.
   if (!GetIsPresenting()) {
     HandleInput();
   }
@@ -189,15 +189,47 @@ VRHMDSensorState::CalcViewMatrices(const
 
   gfx::Matrix4x4 matHead;
   if (flags & VRDisplayCapabilityFlags::Cap_Orientation) {
     matHead.SetRotationFromQuaternion(gfx::Quaternion(orientation[0], orientation[1],
                                                       orientation[2], orientation[3]));
   }
   matHead.PreTranslate(-position[0], -position[1], -position[2]);
 
-  gfx::Matrix4x4 matView = matHead * aHeadToEyeTransforms[VRDisplayInfo::Eye_Left];
+  gfx::Matrix4x4 matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Left];
   matView.Normalize();
   memcpy(leftViewMatrix, matView.components, sizeof(matView.components));
-  matView = matHead * aHeadToEyeTransforms[VRDisplayInfo::Eye_Right];
+  matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Right];
   matView.Normalize();
   memcpy(rightViewMatrix, matView.components, sizeof(matView.components));
 }
+
+const IntSize
+VRDisplayInfo::SuggestedEyeResolution() const
+{
+  return IntSize(mDisplayState.mEyeResolution.width,
+                 mDisplayState.mEyeResolution.height);
+}
+
+const Point3D
+VRDisplayInfo::GetEyeTranslation(uint32_t whichEye) const
+{
+  return Point3D(mDisplayState.mEyeTranslation[whichEye].x,
+                 mDisplayState.mEyeTranslation[whichEye].y,
+                 mDisplayState.mEyeTranslation[whichEye].z);
+}
+
+const Size
+VRDisplayInfo::GetStageSize() const
+{
+  return Size(mDisplayState.mStageSize.width,
+              mDisplayState.mStageSize.height);
+}
+
+const Matrix4x4
+VRDisplayInfo::GetSittingToStandingTransform() const
+{
+  Matrix4x4 m;
+  // If we could replace Matrix4x4 with a pod type, we could
+  // use it directly from the VRDisplayInfo struct.
+  memcpy(m.components, mDisplayState.mSittingToStandingTransform, sizeof(float) * 16);
+  return m;
+}
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_VR_H
 #define GFX_VR_H
 
+#include "moz_external_vr.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/TimeStamp.h"
@@ -27,243 +28,86 @@ enum class GamepadHand : uint8_t;
 struct GamepadPoseState;
 }
 namespace gfx {
 class VRLayerParent;
 class VRDisplayHost;
 class VRControllerHost;
 class VRManagerPromise;
 
-enum class VRDeviceType : uint16_t {
-  Oculus,
-  OpenVR,
-  OSVR,
-  GVR,
-  Puppet,
-  NumVRDeviceTypes
-};
-
-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,
-  /**
-    * Cap_Orientation is set if the VRDisplay is capable of tracking its orientation.
-    */
-  Cap_Orientation = 1 << 2,
-  /**
-   * Cap_Present is set if the VRDisplay is capable of presenting content to an
-   * HMD or similar device.  Can be used to indicate "magic window" devices that
-   * are capable of 6DoF tracking but for which requestPresent is not meaningful.
-   * If false then calls to requestPresent should always fail, and
-   * getEyeParameters should return null.
-   */
-  Cap_Present = 1 << 3,
-  /**
-   * Cap_External is set if the VRDisplay is separate from the device's
-   * primary display. If presenting VR content will obscure
-   * other content on the device, this should be un-set. When
-   * un-set, the application should not attempt to mirror VR content
-   * or update non-VR UI because that content will not be visible.
-   */
-  Cap_External = 1 << 4,
-  /**
-   * Cap_AngularAcceleration is set if the VRDisplay is capable of tracking its
-   * angular acceleration.
-   */
-  Cap_AngularAcceleration = 1 << 5,
-  /**
-   * Cap_LinearAcceleration is set if the VRDisplay is capable of tracking its
-   * linear acceleration.
-   */
-  Cap_LinearAcceleration = 1 << 6,
-  /**
-   * Cap_StageParameters is set if the VRDisplay is capable of room scale VR
-   * and can report the StageParameters to describe the space.
-   */
-  Cap_StageParameters = 1 << 7,
-  /**
-   * Cap_MountDetection is set if the VRDisplay is capable of sensing when the
-   * user is wearing the device.
-   */
-  Cap_MountDetection = 1 << 8,
-  /**
-   * Cap_All used for validity checking during IPC serialization
-   */
-  Cap_All = (1 << 9) - 1
-};
-
-MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
-
-struct VRFieldOfView {
-  VRFieldOfView() = default;
-  VRFieldOfView(double up, double right, double down, double left)
-    : upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
-  {}
-
-  void SetFromTanRadians(double up, double right, double down, double left)
-  {
-    upDegrees = atan(up) * 180.0 / M_PI;
-    rightDegrees = atan(right) * 180.0 / M_PI;
-    downDegrees = atan(down) * 180.0 / M_PI;
-    leftDegrees = atan(left) * 180.0 / M_PI;
-  }
-
-  bool operator==(const VRFieldOfView& other) const {
-    return other.upDegrees == upDegrees &&
-           other.downDegrees == downDegrees &&
-           other.rightDegrees == rightDegrees &&
-           other.leftDegrees == leftDegrees;
-  }
-
-  bool operator!=(const VRFieldOfView& other) const {
-    return !(*this == other);
-  }
-
-  bool IsZero() const {
-    return upDegrees == 0.0 ||
-      rightDegrees == 0.0 ||
-      downDegrees == 0.0 ||
-      leftDegrees == 0.0;
-  }
-
-  Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar, bool rightHanded) const;
-
-  double upDegrees;
-  double rightDegrees;
-  double downDegrees;
-  double leftDegrees;
-};
-
-struct VRHMDSensorState {
-  int64_t inputFrameID;
-  double timestamp;
-  VRDisplayCapabilityFlags flags;
-
-  // These members will only change with inputFrameID:
-  float orientation[4];
-  float position[3];
-  float leftViewMatrix[16];
-  float rightViewMatrix[16];
-  float angularVelocity[3];
-  float angularAcceleration[3];
-  float linearVelocity[3];
-  float linearAcceleration[3];
-
-  void Clear() {
-    memset(this, 0, sizeof(VRHMDSensorState));
-  }
-
-  bool operator==(const VRHMDSensorState& other) const {
-    return inputFrameID == other.inputFrameID &&
-           timestamp == other.timestamp;
-  }
-
-  bool operator!=(const VRHMDSensorState& other) const {
-    return !(*this == other);
-  }
-  void CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms);
-};
-
 // The maximum number of frames of latency that we would expect before we
 // should give up applying pose prediction.
 // If latency is greater than one second, then the experience is not likely
 // to be corrected by pose prediction.  Setting this value too
 // high may result in unnecessary memory allocation.
 // As the current fastest refresh rate is 90hz, 100 is selected as a
 // conservative value.
 static const int kVRMaxLatencyFrames = 100;
 
-// 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;
+enum class VRDeviceType : uint16_t {
+  Oculus,
+  OpenVR,
+  OSVR,
+  GVR,
+  Puppet,
+  External,
+  NumVRDeviceTypes
+};
 
 struct VRDisplayInfo
 {
+  uint32_t mDisplayID;
+  VRDeviceType mType;
+  uint32_t mPresentingGroups;
+  uint32_t mGroupMask;
+  uint64_t mFrameId;
+  uint32_t mPresentingGeneration;
+  VRDisplayState mDisplayState;
+
+  VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
+  const VRHMDSensorState& GetSensorState() const
+  {
+    return mLastSensorState[mFrameId % kVRMaxLatencyFrames];
+  }
+
   VRDeviceType GetType() const { return mType; }
   uint32_t GetDisplayID() const { return mDisplayID; }
-  const nsCString& GetDisplayName() const { return mDisplayName; }
-  VRDisplayCapabilityFlags GetCapabilities() const { return mCapabilityFlags; }
+  const char* GetDisplayName() const { return mDisplayState.mDisplayName; }
+  VRDisplayCapabilityFlags GetCapabilities() const { return mDisplayState.mCapabilityFlags; }
 
-  const IntSize& SuggestedEyeResolution() const { return mEyeResolution; }
-  const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; }
-  const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const { return mEyeFOV[whichEye]; }
-  bool GetIsConnected() const { return mIsConnected; }
-  bool GetIsMounted() const { return mIsMounted; }
+  const IntSize SuggestedEyeResolution() const;
+  const Point3D GetEyeTranslation(uint32_t whichEye) const;
+  const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const { return mDisplayState.mEyeFOV[whichEye]; }
+  bool GetIsConnected() const { return mDisplayState.mIsConnected; }
+  bool GetIsMounted() const { return mDisplayState.mIsMounted; }
   uint32_t GetPresentingGroups() const { return mPresentingGroups; }
   uint32_t GetGroupMask() const { return mGroupMask; }
-  const Size& GetStageSize() const { return mStageSize; }
-  const Matrix4x4& GetSittingToStandingTransform() const { return mSittingToStandingTransform; }
+  const Size GetStageSize() const;
+  const Matrix4x4 GetSittingToStandingTransform() const;
   uint64_t GetFrameId() const { return mFrameId; }
 
-  enum Eye {
-    Eye_Left,
-    Eye_Right,
-    NumEyes
-  };
-
-  uint32_t mDisplayID;
-  VRDeviceType mType;
-  nsCString mDisplayName;
-  VRDisplayCapabilityFlags mCapabilityFlags;
-  VRFieldOfView mEyeFOV[VRDisplayInfo::NumEyes];
-  Point3D mEyeTranslation[VRDisplayInfo::NumEyes];
-  IntSize mEyeResolution;
-  bool mIsConnected;
-  bool mIsMounted;
-  uint32_t mPresentingGroups;
-  uint32_t mGroupMask;
-  Size mStageSize;
-  Matrix4x4 mSittingToStandingTransform;
-  uint64_t mFrameId;
-  uint32_t mPresentingGeneration;
-  VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
-
   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
     return mType == other.mType &&
            mDisplayID == other.mDisplayID &&
-           mDisplayName == other.mDisplayName &&
-           mCapabilityFlags == other.mCapabilityFlags &&
-           mEyeResolution == other.mEyeResolution &&
-           mIsConnected == other.mIsConnected &&
-           mIsMounted == other.mIsMounted &&
+           memcmp(&mDisplayState, &other.mDisplayState, sizeof(VRDisplayState)) == 0 &&
            mPresentingGroups == other.mPresentingGroups &&
            mGroupMask == other.mGroupMask &&
-           mEyeFOV[0] == other.mEyeFOV[0] &&
-           mEyeFOV[1] == other.mEyeFOV[1] &&
-           mEyeTranslation[0] == other.mEyeTranslation[0] &&
-           mEyeTranslation[1] == other.mEyeTranslation[1] &&
-           mStageSize == other.mStageSize &&
-           mSittingToStandingTransform == other.mSittingToStandingTransform &&
            mFrameId == other.mFrameId &&
            mPresentingGeneration == other.mPresentingGeneration;
   }
 
   bool operator!=(const VRDisplayInfo& other) const {
     return !(*this == other);
   }
-
-  const VRHMDSensorState& GetSensorState() const
-  {
-    return mLastSensorState[mFrameId % kVRMaxLatencyFrames];
-  }
 };
 
 struct VRSubmitFrameResultInfo
 {
   VRSubmitFrameResultInfo()
    : mFrameNum(0),
      mWidth(0),
      mHeight(0)
@@ -275,44 +119,39 @@ struct VRSubmitFrameResultInfo
   uint32_t mWidth;
   uint32_t mHeight;
 };
 
 struct VRControllerInfo
 {
   VRDeviceType GetType() const { return mType; }
   uint32_t GetControllerID() const { return mControllerID; }
-  const nsCString& GetControllerName() const { return mControllerName; }
+  const char* GetControllerName() const { return mControllerState.mControllerName; }
   dom::GamepadMappingType GetMappingType() const { return mMappingType; }
   uint32_t GetDisplayID() const { return mDisplayID; }
-  dom::GamepadHand GetHand() const { return mHand; }
-  uint32_t GetNumButtons() const { return mNumButtons; }
-  uint32_t GetNumAxes() const { return mNumAxes; }
-  uint32_t GetNumHaptics() const { return mNumHaptics; }
+  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; }
 
   uint32_t mControllerID;
   VRDeviceType mType;
-  nsCString mControllerName;
   dom::GamepadMappingType mMappingType;
   uint32_t mDisplayID;
-  dom::GamepadHand mHand;
-  uint32_t mNumButtons;
-  uint32_t mNumAxes;
-  uint32_t mNumHaptics;
-
+  VRControllerState mControllerState;
   bool operator==(const VRControllerInfo& other) const {
     return mType == other.mType &&
            mControllerID == other.mControllerID &&
-           mControllerName == other.mControllerName &&
+           strncmp(mControllerState.mControllerName, other.mControllerState.mControllerName, kVRControllerNameMaxLen) == 0 &&
            mMappingType == other.mMappingType &&
            mDisplayID == other.mDisplayID &&
-           mHand == other.mHand &&
-           mNumButtons == other.mNumButtons &&
-           mNumAxes == other.mNumAxes &&
-           mNumHaptics == other.mNumHaptics;
+           mControllerState.mHand == other.mControllerState.mHand &&
+           mControllerState.mNumButtons == other.mControllerState.mNumButtons &&
+           mControllerState.mNumAxes == other.mControllerState.mNumAxes &&
+           mControllerState.mNumHaptics == other.mControllerState.mNumHaptics;
   }
 
   bool operator!=(const VRControllerInfo& other) const {
     return !(*this == other);
   }
 };
 
 struct VRTelemetry
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -0,0 +1,470 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <math.h>
+
+#include "prlink.h"
+#include "prenv.h"
+#include "gfxPrefs.h"
+#include "mozilla/Preferences.h"
+
+#include "mozilla/gfx/Quaternion.h"
+
+#ifdef XP_WIN
+#include "CompositorD3D11.h"
+#include "TextureD3D11.h"
+static const char* kShmemName = "moz.gecko.vr_ext.0.0.1";
+#elif defined(XP_MACOSX)
+#include "mozilla/gfx/MacIOSurface.h"
+#include <sys/mman.h>
+#include <sys/stat.h>        /* For mode constants */
+#include <fcntl.h>           /* For O_* constants */
+#include <errno.h>
+static const char* kShmemName = "/moz.gecko.vr_ext.0.0.1";
+#endif
+
+#include "gfxVRExternal.h"
+#include "VRManagerParent.h"
+#include "VRManager.h"
+#include "VRThread.h"
+
+#include "nsServiceManagerUtils.h"
+#include "nsIScreenManager.h"
+
+#include "mozilla/dom/GamepadEventTypes.h"
+#include "mozilla/dom/GamepadBinding.h"
+#include "mozilla/Telemetry.h"
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#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;
+}
+
+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);
+}
+
+VRHMDSensorState
+VRDisplayExternal::GetSensorState()
+{
+  VRManager *vm = VRManager::Get();
+  VRSystemManagerExternal* manager = vm->GetExternalManager();
+
+  manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState);
+
+//  result.CalcViewMatrices(headToEyeTransforms);
+  mLastSensorState.inputFrameID = mDisplayInfo.mFrameId;
+  return mLastSensorState;
+}
+
+void
+VRDisplayExternal::StartPresentation()
+{
+  if (mIsPresenting) {
+    return;
+  }
+  mIsPresenting = true;
+  mTelemetry.Clear();
+  mTelemetry.mPresentationStart = TimeStamp::Now();
+
+  // TODO - Implement this
+
+  // mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
+}
+
+void
+VRDisplayExternal::StopPresentation()
+{
+  if (!mIsPresenting) {
+    return;
+  }
+
+  // TODO - Implement this
+
+/*
+  mIsPresenting = false;
+  const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
+  Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
+  Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
+                        duration.ToMilliseconds());
+
+  ::vr::Compositor_CumulativeStats stats;
+  mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
+  const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
+                                        mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
+  Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
+*/
+}
+
+#if defined(XP_WIN)
+
+bool
+VRDisplayExternal::SubmitFrame(ID3D11Texture2D* aSource,
+                             const IntSize& aSize,
+                             const gfx::Rect& aLeftEyeRect,
+                             const gfx::Rect& aRightEyeRect)
+{
+  // FINDME!  Implement this
+  return false;
+}
+
+#elif defined(XP_MACOSX)
+
+bool
+VRDisplayExternal::SubmitFrame(MacIOSurface* aMacIOSurface,
+                             const IntSize& aSize,
+                             const gfx::Rect& aLeftEyeRect,
+                             const gfx::Rect& aRightEyeRect)
+{
+  const void* ioSurface = aMacIOSurface->GetIOSurfacePtr();
+  bool result = false;
+  if (ioSurface == nullptr) {
+    NS_WARNING("VRDisplayExternal::SubmitFrame() could not get an IOSurface");
+  } else {
+    // FINDME!  Implement this
+  }
+  return result;
+}
+
+#endif
+
+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()
+ : mExternalShmem(nullptr)
+{
+#if defined(XP_MACOSX)
+  mShmemFD = 0;
+#elif defined(XP_WIN)
+  mShmemFile = NULL;
+#endif
+}
+
+VRSystemManagerExternal::~VRSystemManagerExternal()
+{
+  CloseShmem();
+}
+
+void
+VRSystemManagerExternal::OpenShmem()
+{
+  if (mExternalShmem) {
+    return;
+  }
+  fprintf(stderr, "Opening Shmem...\n");
+#if defined(XP_MACOSX)
+  
+  if (mShmemFD == 0) {
+    mShmemFD = shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
+  }
+  if (mShmemFD <= 0) {
+    fprintf(stderr, "shm_open error: %i\n", errno); // findme! kip! hack!  Need to properly log this...
+    mShmemFD = 0;
+    return;
+  }
+
+  struct stat sb;
+  fstat(mShmemFD, &sb);
+  off_t length = sb.st_size;
+  if (length < (off_t)sizeof(VRExternalShmem)) {
+    fprintf(stderr, "shmem size is smaller than sizeof(VRExternalShmem)\n");
+    CloseShmem();
+    return;
+  }
+
+  mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, mShmemFD, 0);
+  if (mExternalShmem == MAP_FAILED) {
+    fprintf(stderr, "mmap failed for VRExternalShmem\n");
+    mExternalShmem = NULL;
+    CloseShmem();
+    return;
+  }
+
+#elif defined(XP_WIN)
+
+  if (mShmemFile == NULL) {
+    mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName);
+    if (mShmemFile == NULL) {
+      fprintf(stderr, "Could not open file mapping object (%d).\n",
+              GetLastError());
+      CloseShmem();
+      return;
+    }
+  }
+  LARGE_INTEGER length;
+  length.QuadPart = sizeof(VRExternalShmem);
+  mExternalShmem = (VRExternalShmem*)MapViewOfFile(mShmemFile, // handle to map object
+    FILE_MAP_ALL_ACCESS,  // read/write permission
+    0,
+    0,
+    length.QuadPart);
+
+  if (mExternalShmem == NULL) {
+    fprintf(stderr, "Could not map view of file (%d).\n",
+            GetLastError());
+    CloseShmem();
+    return;
+  }
+#endif
+  fprintf(stderr, "Opened Shmem\n");
+  CheckForShutdown();
+}
+
+void
+VRSystemManagerExternal::CheckForShutdown()
+{
+  if (mExternalShmem) {
+    if (mExternalShmem->generationA == -1 && mExternalShmem->generationB == -1) {
+      Shutdown();
+    }
+  }
+}
+
+void
+VRSystemManagerExternal::CloseShmem()
+{
+  fprintf(stderr, "Closing Shmem...\n");
+#if defined(XP_MACOSX)
+  
+  if (mExternalShmem) {
+    munmap((void *)mExternalShmem, sizeof(VRExternalShmem));
+    mExternalShmem = NULL;
+  }
+  if (mShmemFD) {
+    close(mShmemFD);
+  }
+  mShmemFD = 0;
+  
+#elif defined(XP_WIN)
+  if (mExternalShmem) {
+    UnmapViewOfFile((void *)mExternalShmem);
+    mExternalShmem = NULL;
+  }
+  if (mShmemFile) {
+    CloseHandle(mShmemFile);
+    mShmemFile = NULL;
+  }
+#endif
+  fprintf(stderr, "Closed Shmem\n");
+}
+
+/*static*/ already_AddRefed<VRSystemManagerExternal>
+VRSystemManagerExternal::Create()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gfxPrefs::VREnabled() || !gfxPrefs::VRExternalEnabled()) {
+    return nullptr;
+  }
+
+  RefPtr<VRSystemManagerExternal> manager = new VRSystemManagerExternal();
+  return manager.forget();
+}
+
+void
+VRSystemManagerExternal::Destroy()
+{
+  Shutdown();
+}
+
+void
+VRSystemManagerExternal::Shutdown()
+{
+  if (mDisplay) {
+    mDisplay = nullptr;
+  }
+  RemoveControllers();
+  CloseShmem();
+}
+
+void
+VRSystemManagerExternal::NotifyVSync()
+{
+  VRSystemManager::NotifyVSync();
+
+  CheckForShutdown();
+
+  if (mDisplay) {
+    mDisplay->Refresh();
+  }
+}
+
+void
+VRSystemManagerExternal::Enumerate()
+{
+  if (mDisplay == nullptr) {
+    OpenShmem();
+    if (mExternalShmem) {
+      VRDisplayState displayState;
+      PullState(&displayState);
+      if (displayState.mIsConnected) {
+        mDisplay = new VRDisplayExternal(displayState);
+      }
+    }
+  }
+}
+
+bool
+VRSystemManagerExternal::ShouldInhibitEnumeration()
+{
+  if (VRSystemManager::ShouldInhibitEnumeration()) {
+    return true;
+  }
+  if (mDisplay) {
+    // When we find an a VR device, don't
+    // allow any further enumeration as it
+    // may get picked up redundantly by other
+    // API's.
+    return true;
+  }
+  return false;
+}
+
+void
+VRSystemManagerExternal::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
+{
+  if (mDisplay) {
+    aHMDResult.AppendElement(mDisplay);
+  }
+}
+
+bool
+VRSystemManagerExternal::GetIsPresenting()
+{
+  if (mDisplay) {
+    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
+}
+
+void
+VRSystemManagerExternal::StopVibrateHaptic(uint32_t aControllerIdx)
+{
+  // TODO - Implement this
+}
+
+void
+VRSystemManagerExternal::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
+{
+  aControllerResult.Clear();
+  for (uint32_t i = 0; i < mExternalController.Length(); ++i) {
+    aControllerResult.AppendElement(mExternalController[i]);
+  }
+}
+
+void
+VRSystemManagerExternal::ScanForControllers()
+{
+  // TODO - Implement this
+}
+
+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;
+}
+
+void
+VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState /* = nullptr */)
+{
+  MOZ_ASSERT(mExternalShmem);
+  if (mExternalShmem) {
+    // FINDME!!! TODO!! HACK!! KIP!! Add locking here
+    // for non-x86 platforms, such as a spinlock
+    VRExternalShmem tmp;
+    memcpy(&tmp, (void *)mExternalShmem, sizeof(VRExternalShmem));
+    if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1) {
+      memcpy(aDisplayState, &tmp.state.displayState, sizeof(VRDisplayState));
+      if (aSensorState) {
+        memcpy(aSensorState, &tmp.state.sensorState, sizeof(VRHMDSensorState));
+      }
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/gfx/vr/gfxVRExternal.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_VR_EXTERNAL_H
+#define GFX_VR_EXTERNAL_H
+
+#include "nsTArray.h"
+#include "nsIScreen.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/EnumeratedArray.h"
+
+#include "gfxVR.h"
+#include "VRDisplayHost.h"
+
+#if defined(XP_MACOSX)
+class MacIOSurface;
+#endif
+namespace mozilla {
+namespace gfx {
+class VRThread;
+
+namespace impl {
+
+class VRDisplayExternal : public VRDisplayHost
+{
+public:
+  void ZeroSensor() override;
+
+protected:
+  virtual VRHMDSensorState GetSensorState() override;
+  virtual void StartPresentation() override;
+  virtual void StopPresentation() override;
+#if defined(XP_WIN)
+  virtual bool SubmitFrame(ID3D11Texture2D* aSource,
+                           const IntSize& aSize,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) override;
+#elif defined(XP_MACOSX)
+  virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
+                           const IntSize& aSize,
+                           const gfx::Rect& aLeftEyeRect,
+                           const gfx::Rect& aRightEyeRect) override;
+#endif
+
+public:
+  explicit VRDisplayExternal(const VRDisplayState& aDisplayState);
+  void Refresh();
+protected:
+  virtual ~VRDisplayExternal();
+  void Destroy();
+
+  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();
+
+  virtual void Destroy() override;
+  virtual void Shutdown() override;
+  virtual void NotifyVSync() override;
+  virtual void Enumerate() override;
+  virtual bool ShouldInhibitEnumeration() override;
+  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
+  virtual bool GetIsPresenting() override;
+  virtual void HandleInput() override;
+  virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
+                              aControllerResult) override;
+  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;
+  void PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState = nullptr);
+
+protected:
+  VRSystemManagerExternal();
+  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;
+#endif
+  volatile VRExternalShmem* mExternalShmem;
+
+  void OpenShmem();
+  void CloseShmem();
+  void CheckForShutdown();
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+
+#endif /* GFX_VR_EXTERNAL_H */
--- a/gfx/vr/gfxVRGVR.cpp
+++ b/gfx/vr/gfxVRGVR.cpp
@@ -208,17 +208,17 @@ VRDisplayGVR::VRDisplayGVR()
   , mSwapChain(nullptr)
   , mFrameBufferSize{0, 0}
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayGVR, VRDisplayHost);
   MOZ_ASSERT(GetNonPresentingContext());
   MOZ_ASSERT(!sContextObserver); // There can be only one GVR display at a time.
   sContextObserver = this;
 
-  mDisplayInfo.mDisplayName.AssignLiteral("GVR HMD");
+  strncpy(mDisplayInfo.mDisplayName, "GVR HMD", kVRDisplayNameMaxLen);
   mDisplayInfo.mIsConnected = true;
   mDisplayInfo.mIsMounted = true;
   mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
                                   VRDisplayCapabilityFlags::Cap_Orientation |
                                   VRDisplayCapabilityFlags::Cap_Position | // Not yet...
                                   VRDisplayCapabilityFlags::Cap_Present;
 
   GVR_CHECK(gvr_refresh_viewer_profile(GetNonPresentingContext()));
@@ -404,17 +404,17 @@ VRDisplayGVR::GetSensorState()
   result.position[0] = m._14;
   result.position[1] = m._24;
   result.position[2] = m._34;
   result.linearVelocity[0] = 0.0f;
   result.linearVelocity[1] = 0.0f;
   result.linearVelocity[2] = 0.0f;
 
   UpdateHeadToEye(context);
-  result.CalcViewMatrices(mHeadToEyes);
+  CalcViewMatrices(&result, mHeadToEyes);
 
   return result;
 }
 
 void
 VRDisplayGVR::SetPaused(const bool aPaused)
 {
   if (aPaused) {
@@ -591,22 +591,25 @@ VRDisplayGVR::GetControllers(nsTArray<Re
 {
   aControllerResult.AppendElement(mController.get());
 }
 
 VRControllerGVR::VRControllerGVR(dom::GamepadHand aHand, uint32_t aDisplayID)
   : VRControllerHost(VRDeviceType::GVR, aHand, aDisplayID)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerGVR, VRControllerHost);
-  mControllerInfo.mControllerName.AssignLiteral("Daydream Controller");
+
+  VRControlerState& state = mControllerInfo.mControllerState;
+  strncpy(state.mControllerName, "Daydream Controller", kVRControllerNameMaxLen);
+
   // The gvr_controller_button enum starts with GVR_CONTROLLER_BUTTON_NONE at index zero
   // so the GVR controller has one less button than GVR_CONTROLLER_BUTTON_COUNT specifies.
-  mControllerInfo.mNumButtons = GVR_CONTROLLER_BUTTON_COUNT - 1; // Skip dummy none button
-  mControllerInfo.mNumAxes = 2;
-  mControllerInfo.mNumHaptics = 0;
+  state.mNumButtons = GVR_CONTROLLER_BUTTON_COUNT - 1; // Skip dummy none button
+  state.mNumAxes = 2;
+  state.mNumHaptics = 0;
 }
 
 VRControllerGVR::~VRControllerGVR()
 {
   MOZ_COUNT_DTOR_INHERITED(VRControllerGVR, VRControllerHost);
 }
 
 void
--- a/gfx/vr/gfxVROSVR.cpp
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -208,64 +208,65 @@ VRDisplayOSVR::VRDisplayOSVR(OSVR_Client
   : VRDisplayHost(VRDeviceType::OSVR)
   , m_ctx(context)
   , m_iface(iface)
   , m_display(display)
 {
 
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOSVR, VRDisplayHost);
 
-  mDisplayInfo.mIsConnected = true;
-  mDisplayInfo.mDisplayName.AssignLiteral("OSVR HMD");
-  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
-  mDisplayInfo.mCapabilityFlags =
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
+  state.mIsConnected = true;
+  strncpy(state.mDisplayName, "OSVR HMD", kVRDisplayNameMaxLen);
+  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
+  state.mCapabilityFlags =
     VRDisplayCapabilityFlags::Cap_Orientation | VRDisplayCapabilityFlags::Cap_Position;
 
-  mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
-  mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
 
   // XXX OSVR display topology allows for more than one viewer
   // will assume only one viewer for now (most likely stay that way)
 
   OSVR_EyeCount numEyes;
   osvr_ClientGetNumEyesForViewer(*m_display, 0, &numEyes);
 
   for (uint8_t eye = 0; eye < numEyes; eye++) {
     double left, right, bottom, top;
     // XXX for now there is only one surface per eye
     osvr_ClientGetViewerEyeSurfaceProjectionClippingPlanes(
       *m_display, 0, eye, 0, &left, &right, &bottom, &top);
-    mDisplayInfo.mEyeFOV[eye] =
+    state.mEyeFOV[eye] =
       SetFromTanRadians(-left, right, -bottom, top);
   }
 
   // XXX Assuming there is only one display input for now
   // however, it's possible to have more than one (dSight with 2 HDMI inputs)
   OSVR_DisplayDimension width, height;
   osvr_ClientGetDisplayDimensions(*m_display, 0, &width, &height);
 
 
   for (uint8_t eye = 0; eye < numEyes; eye++) {
 
     OSVR_ViewportDimension l, b, w, h;
     osvr_ClientGetRelativeViewportForViewerEyeSurface(*m_display, 0, eye, 0, &l,
                                                       &b, &w, &h);
-    mDisplayInfo.mEyeResolution.width = w;
-    mDisplayInfo.mEyeResolution.height = h;
+    state.mEyeResolution.width = w;
+    state.mEyeResolution.height = h;
     OSVR_Pose3 eyePose;
     // Viewer eye pose may not be immediately available, update client context until we get it
     OSVR_ReturnCode ret =
       osvr_ClientGetViewerEyePose(*m_display, 0, eye, &eyePose);
     while (ret != OSVR_RETURN_SUCCESS) {
       osvr_ClientUpdate(*m_ctx);
       ret = osvr_ClientGetViewerEyePose(*m_display, 0, eye, &eyePose);
     }
-    mDisplayInfo.mEyeTranslation[eye].x = eyePose.translation.data[0];
-    mDisplayInfo.mEyeTranslation[eye].y = eyePose.translation.data[1];
-    mDisplayInfo.mEyeTranslation[eye].z = eyePose.translation.data[2];
+    state.mEyeTranslation[eye].x = eyePose.translation.data[0];
+    state.mEyeTranslation[eye].y = eyePose.translation.data[1];
+    state.mEyeTranslation[eye].z = eyePose.translation.data[2];
 
     Matrix4x4 pose;
     pose.SetRotationFromQuaternion(gfx::Quaternion(osvrQuatGetX(&eyePose.rotation),
                                                    osvrQuatGetY(&eyePose.rotation),
                                                    osvrQuatGetZ(&eyePose.rotation),
                                                    osvrQuatGetW(&eyePose.rotation)));
     pose.PreTranslate(eyePose.translation.data[0], eyePose.translation.data[1], eyePose.translation.data[2]);
     pose.Invert();
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -801,54 +801,54 @@ VRDisplayOculus::VRDisplayOculus(VROculu
   , mLinearSamplerState(nullptr)
   , mVSConstantBuffer(nullptr)
   , mPSConstantBuffer(nullptr)
   , mVertexBuffer(nullptr)
   , mInputLayout(nullptr)
   , mEyeHeight(OVR_DEFAULT_EYE_HEIGHT)
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
-
-  mDisplayInfo.mDisplayName.AssignLiteral("Oculus VR HMD");
-  mDisplayInfo.mIsConnected = true;
-  mDisplayInfo.mIsMounted = false;
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
+  strncpy(state.mDisplayName, "Oculus VR HMD", kVRDisplayNameMaxLen);
+  state.mIsConnected = true;
+  state.mIsMounted = false;
 
   mDesc = ovr_GetHmdDesc(aSession->Get());
 
-  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
+  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
   if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
-    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
   }
   if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
-    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
-    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
-    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
   }
-  mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
-  mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
-  mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
 
-  mFOVPort[VRDisplayInfo::Eye_Left] = mDesc.DefaultEyeFov[ovrEye_Left];
-  mFOVPort[VRDisplayInfo::Eye_Right] = mDesc.DefaultEyeFov[ovrEye_Right];
+  mFOVPort[VRDisplayState::Eye_Left] = mDesc.DefaultEyeFov[ovrEye_Left];
+  mFOVPort[VRDisplayState::Eye_Right] = mDesc.DefaultEyeFov[ovrEye_Right];
 
-  mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Left] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Left]);
-  mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Right] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Right]);
+  state.mEyeFOV[VRDisplayState::Eye_Left] = FromFovPort(mFOVPort[VRDisplayState::Eye_Left]);
+  state.mEyeFOV[VRDisplayState::Eye_Right] = FromFovPort(mFOVPort[VRDisplayState::Eye_Right]);
 
   float pixelsPerDisplayPixel = 1.0;
   ovrSizei texSize[2];
 
   // get eye texture sizes
-  for (uint32_t eye = 0; eye < VRDisplayInfo::NumEyes; eye++) {
+  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
     texSize[eye] = ovr_GetFovTextureSize(mSession->Get(), (ovrEyeType)eye, mFOVPort[eye], pixelsPerDisplayPixel);
   }
 
   // take the max of both for eye resolution
-  mDisplayInfo.mEyeResolution.width = std::max(texSize[VRDisplayInfo::Eye_Left].w, texSize[VRDisplayInfo::Eye_Right].w);
-  mDisplayInfo.mEyeResolution.height = std::max(texSize[VRDisplayInfo::Eye_Left].h, texSize[VRDisplayInfo::Eye_Right].h);
+  state.mEyeResolution.width = std::max(texSize[VRDisplayState::Eye_Left].w, texSize[VRDisplayState::Eye_Right].w);
+  state.mEyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h, texSize[VRDisplayState::Eye_Right].h);
 
   UpdateEyeParameters();
   UpdateStageParameters();
 }
 
 VRDisplayOculus::~VRDisplayOculus() {
   Destroy();
   MOZ_COUNT_DTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
@@ -861,73 +861,75 @@ VRDisplayOculus::Destroy()
   mSession = nullptr;
 }
 
 void
 VRDisplayOculus::UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms /* = nullptr */)
 {
   // Note this must be called every frame, as the IPD adjustment can be changed
   // by the user during a VR session.
-  for (uint32_t eye = 0; eye < VRDisplayInfo::NumEyes; eye++) {
+  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
     // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to return the updated
     // version of ovrEyeRenderDesc.  This is normally done by the Oculus static lib shim, but we
     // need to do this explicitly as we are loading the Oculus runtime dll directly.
     ovrEyeRenderDesc renderDesc = ovr_GetRenderDesc2(mSession->Get(), (ovrEyeType)eye, mFOVPort[eye]);
-    mDisplayInfo.mEyeTranslation[eye].x = renderDesc.HmdToEyePose.Position.x;
-    mDisplayInfo.mEyeTranslation[eye].y = renderDesc.HmdToEyePose.Position.y;
-    mDisplayInfo.mEyeTranslation[eye].z = renderDesc.HmdToEyePose.Position.z;
+    VRDisplayState& state = mDisplayInfo.mDisplayState;
+    state.mEyeTranslation[eye].x = renderDesc.HmdToEyePose.Position.x;
+    state.mEyeTranslation[eye].y = renderDesc.HmdToEyePose.Position.y;
+    state.mEyeTranslation[eye].z = renderDesc.HmdToEyePose.Position.z;
     if (aHeadToEyeTransforms) {
       Matrix4x4 pose;
       pose.SetRotationFromQuaternion(gfx::Quaternion(renderDesc.HmdToEyePose.Orientation.x, renderDesc.HmdToEyePose.Orientation.y, renderDesc.HmdToEyePose.Orientation.z, renderDesc.HmdToEyePose.Orientation.w));
       pose.PreTranslate(renderDesc.HmdToEyePose.Position.x, renderDesc.HmdToEyePose.Position.y, renderDesc.HmdToEyePose.Position.z);
       pose.Invert();
       aHeadToEyeTransforms[eye] = pose;
     }
   }
 }
 
 void
 VRDisplayOculus::UpdateStageParameters()
 {
   if (!mSession->IsTrackingReady()) {
     return;
   }
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
   ovrVector3f playArea;
   ovrResult res = ovr_GetBoundaryDimensions(mSession->Get(), ovrBoundary_PlayArea, &playArea);
   if (res == ovrSuccess) {
-    mDisplayInfo.mStageSize.width = playArea.x;
-    mDisplayInfo.mStageSize.height = playArea.z;
+    state.mStageSize.width = playArea.x;
+    state.mStageSize.height = playArea.z;
   } else {
     // If we fail, fall back to reasonable defaults.
     // 1m x 1m space
-    mDisplayInfo.mStageSize.width = 1.0f;
-    mDisplayInfo.mStageSize.height = 1.0f;
+    state.mStageSize.width = 1.0f;
+    state.mStageSize.height = 1.0f;
   }
 
   mEyeHeight = ovr_GetFloat(mSession->Get(), OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
 
-  mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
-  mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
+  state.mSittingToStandingTransform[0] = 1.0f;
+  state.mSittingToStandingTransform[1] = 0.0f;
+  state.mSittingToStandingTransform[2] = 0.0f;
+  state.mSittingToStandingTransform[3] = 0.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
-  mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
+  state.mSittingToStandingTransform[4] = 0.0f;
+  state.mSittingToStandingTransform[5] = 1.0f;
+  state.mSittingToStandingTransform[6] = 0.0f;
+  state.mSittingToStandingTransform[7] = 0.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
-  mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
+  state.mSittingToStandingTransform[8] = 0.0f;
+  state.mSittingToStandingTransform[9] = 0.0f;
+  state.mSittingToStandingTransform[10] = 1.0f;
+  state.mSittingToStandingTransform[11] = 0.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._42 = mEyeHeight;
-  mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
+  state.mSittingToStandingTransform[12] = 0.0f;
+  state.mSittingToStandingTransform[13] = mEyeHeight;
+  state.mSittingToStandingTransform[14] = 0.0f;
+  state.mSittingToStandingTransform[15] = 1.0f;
 }
 
 void
 VRDisplayOculus::ZeroSensor()
 {
   if (!mSession->IsTrackingReady()) {
     return;
   }
@@ -1014,17 +1016,17 @@ VRDisplayOculus::GetSensorState(double a
 }
 
 void
 VRDisplayOculus::StartPresentation()
 {
   if (!CreateD3DObjects()) {
     return;
   }
-  mSession->StartPresentation(IntSize(mDisplayInfo.mEyeResolution.width * 2, mDisplayInfo.mEyeResolution.height));
+  mSession->StartPresentation(IntSize(mDisplayInfo.mDisplayState.mEyeResolution.width * 2, mDisplayInfo.mDisplayState.mEyeResolution.height));
   if (!mSession->IsPresentationReady()) {
     return;
   }
 
   if (!mQuadVS) {
     if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
       NS_WARNING("Failed to create vertex shader for Oculus");
       return;
@@ -1294,52 +1296,55 @@ VRDisplayOculus::SubmitFrame(ID3D11Textu
 
   mSession->mSubmitThread = mSubmitThread;
   return true;
 }
 
 void
 VRDisplayOculus::Refresh()
 {
-  mDisplayInfo.mIsConnected = mSession->IsTrackingReady();
-  mDisplayInfo.mIsMounted = mSession->IsMounted();
+  mDisplayInfo.mDisplayState.mIsConnected = mSession->IsTrackingReady();
+  mDisplayInfo.mDisplayState.mIsMounted = mSession->IsMounted();
 }
 
 VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand, uint32_t aDisplayID)
   : VRControllerHost(VRDeviceType::Oculus, aHand, aDisplayID)
   , mIndexTrigger(0.0f)
   , mHandTrigger(0.0f)
   , mVibrateThread(nullptr)
   , mIsVibrateStopped(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost);
 
+  VRControllerState& state = mControllerInfo.mControllerState;
+
   char* touchID = "";
   switch (aHand) {
     case dom::GamepadHand::Left:
       touchID = "Oculus Touch (Left)";
       break;
     case dom::GamepadHand::Right:
       touchID = "Oculus Touch (Right)";
       break;
     default:
       MOZ_ASSERT(false);
       break;
   }
-  mControllerInfo.mControllerName = touchID;
+
+  strncpy(state.mControllerName, touchID, kVRControllerNameMaxLen);
 
   MOZ_ASSERT(kNumOculusButton ==
              static_cast<uint32_t>(OculusLeftControllerButtonType::NumButtonType)
              && kNumOculusButton ==
              static_cast<uint32_t>(OculusRightControllerButtonType::NumButtonType));
 
-  mControllerInfo.mNumButtons = kNumOculusButton;
-  mControllerInfo.mNumAxes = static_cast<uint32_t>(
-                             OculusControllerAxisType::NumVRControllerAxisType);
-  mControllerInfo.mNumHaptics = kNumOculusHaptcs;
+  state.mNumButtons = kNumOculusButton;
+  state.mNumAxes = static_cast<uint32_t>(
+                   OculusControllerAxisType::NumVRControllerAxisType);
+  state.mNumHaptics = kNumOculusHaptcs;
 }
 
 float
 VRControllerOculus::GetAxisMove(uint32_t aAxis)
 {
   return mAxisMove[aAxis];
 }
 
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -53,46 +53,48 @@ VRDisplayOpenVR::VRDisplayOpenVR(::vr::I
   : VRDisplayHost(VRDeviceType::OpenVR)
   , mVRSystem(aVRSystem)
   , mVRChaperone(aVRChaperone)
   , mVRCompositor(aVRCompositor)
   , mIsPresenting(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
 
-  mDisplayInfo.mDisplayName.AssignLiteral("OpenVR HMD");
-  mDisplayInfo.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
-  mDisplayInfo.mIsMounted = false;
-  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
-                                  VRDisplayCapabilityFlags::Cap_Orientation |
-                                  VRDisplayCapabilityFlags::Cap_Position |
-                                  VRDisplayCapabilityFlags::Cap_External |
-                                  VRDisplayCapabilityFlags::Cap_Present |
-                                  VRDisplayCapabilityFlags::Cap_StageParameters;
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
+
+  strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
+  state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
+  state.mIsMounted = false;
+  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
+                           VRDisplayCapabilityFlags::Cap_Orientation |
+                           VRDisplayCapabilityFlags::Cap_Position |
+                           VRDisplayCapabilityFlags::Cap_External |
+                           VRDisplayCapabilityFlags::Cap_Present |
+                           VRDisplayCapabilityFlags::Cap_StageParameters;
   mIsHmdPresent = ::vr::VR_IsHmdPresent();
 
   ::vr::ETrackedPropertyError err;
   bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
   if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
-    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
   }
 
   mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
 
   uint32_t w, h;
   mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
-  mDisplayInfo.mEyeResolution.width = w;
-  mDisplayInfo.mEyeResolution.height = h;
+  state.mEyeResolution.width = w;
+  state.mEyeResolution.height = h;
 
   // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
   for (uint32_t eye = 0; eye < 2; ++eye) {
     // get l/r/t/b clip plane coordinates
     float l, r, t, b;
     mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &l, &r, &t, &b);
-    mDisplayInfo.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
+    state.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
   }
   UpdateEyeParameters();
   UpdateStageParameters();
 }
 
 VRDisplayOpenVR::~VRDisplayOpenVR()
 {
   Destroy();
@@ -106,22 +108,22 @@ VRDisplayOpenVR::Destroy()
   ::vr::VR_Shutdown();
 }
 
 void
 VRDisplayOpenVR::UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms /* = nullptr */)
 {
   // Note this must be called every frame, as the IPD adjustment can be changed
   // by the user during a VR session.
-  for (uint32_t eye = 0; eye < VRDisplayInfo::NumEyes; eye++) {
+  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
     ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
 
-    mDisplayInfo.mEyeTranslation[eye].x = eyeToHead.m[0][3];
-    mDisplayInfo.mEyeTranslation[eye].y = eyeToHead.m[1][3];
-    mDisplayInfo.mEyeTranslation[eye].z = eyeToHead.m[2][3];
+    mDisplayInfo.mDisplayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
+    mDisplayInfo.mDisplayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
+    mDisplayInfo.mDisplayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
 
     if (aHeadToEyeTransforms) {
       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();
@@ -129,68 +131,69 @@ VRDisplayOpenVR::UpdateEyeParameters(gfx
       aHeadToEyeTransforms[eye] = pose;
     }
   }
 }
 
 void
 VRDisplayOpenVR::UpdateStageParameters()
 {
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
   float sizeX = 0.0f;
   float sizeZ = 0.0f;
   if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
     ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
-    mDisplayInfo.mStageSize.width = sizeX;
-    mDisplayInfo.mStageSize.height = sizeZ;
+    state.mStageSize.width = sizeX;
+    state.mStageSize.height = sizeZ;
 
-    mDisplayInfo.mSittingToStandingTransform._11 = t.m[0][0];
-    mDisplayInfo.mSittingToStandingTransform._12 = t.m[1][0];
-    mDisplayInfo.mSittingToStandingTransform._13 = t.m[2][0];
-    mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
+    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;
 
-    mDisplayInfo.mSittingToStandingTransform._21 = t.m[0][1];
-    mDisplayInfo.mSittingToStandingTransform._22 = t.m[1][1];
-    mDisplayInfo.mSittingToStandingTransform._23 = t.m[2][1];
-    mDisplayInfo.mSittingToStandingTransform._24 = 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;
 
-    mDisplayInfo.mSittingToStandingTransform._31 = t.m[0][2];
-    mDisplayInfo.mSittingToStandingTransform._32 = t.m[1][2];
-    mDisplayInfo.mSittingToStandingTransform._33 = t.m[2][2];
-    mDisplayInfo.mSittingToStandingTransform._34 = 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;
 
-    mDisplayInfo.mSittingToStandingTransform._41 = t.m[0][3];
-    mDisplayInfo.mSittingToStandingTransform._42 = t.m[1][3];
-    mDisplayInfo.mSittingToStandingTransform._43 = t.m[2][3];
-    mDisplayInfo.mSittingToStandingTransform._44 = 1.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;
   } else {
     // If we fail, fall back to reasonable defaults.
     // 1m x 1m space, 0.75m high in seated position
 
-    mDisplayInfo.mStageSize.width = 1.0f;
-    mDisplayInfo.mStageSize.height = 1.0f;
+    state.mStageSize.width = 1.0f;
+    state.mStageSize.height = 1.0f;
 
-    mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
-    mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
+    state.mSittingToStandingTransform[0] = 1.0f;
+    state.mSittingToStandingTransform[1] = 0.0f;
+    state.mSittingToStandingTransform[2] = 0.0f;
+    state.mSittingToStandingTransform[3] = 0.0f;
 
-    mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
-    mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
+    state.mSittingToStandingTransform[4] = 0.0f;
+    state.mSittingToStandingTransform[5] = 1.0f;
+    state.mSittingToStandingTransform[6] = 0.0f;
+    state.mSittingToStandingTransform[7] = 0.0f;
 
-    mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
-    mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
+    state.mSittingToStandingTransform[9] = 0.0f;
+    state.mSittingToStandingTransform[10] = 0.0f;
+    state.mSittingToStandingTransform[11] = 1.0f;
+    state.mSittingToStandingTransform[12] = 0.0f;
 
-    mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._42 = 0.75f;
-    mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
-    mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
+    state.mSittingToStandingTransform[13] = 0.0f;
+    state.mSittingToStandingTransform[14] = 0.75f;
+    state.mSittingToStandingTransform[15] = 0.0f;
+    state.mSittingToStandingTransform[16] = 1.0f;
   }
 }
 
 void
 VRDisplayOpenVR::ZeroSensor()
 {
   mVRSystem->ResetSeatedZeroPose();
   UpdateStageParameters();
@@ -207,32 +210,32 @@ VRDisplayOpenVR::Refresh()
 {
   mIsHmdPresent = ::vr::VR_IsHmdPresent();
 
   ::vr::VREvent_t event;
   while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
     switch (event.eventType) {
       case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mIsMounted = true;
+          mDisplayInfo.mDisplayState.mIsMounted = true;
         }
         break;
       case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mIsMounted = false;
+          mDisplayInfo.mDisplayState.mIsMounted = false;
         }
         break;
       case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mIsConnected = true;
+          mDisplayInfo.mDisplayState.mIsConnected = true;
         }
         break;
       case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
         if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mIsConnected = false;
+          mDisplayInfo.mDisplayState.mIsConnected = false;
         }
         break;
       case ::vr::EVREventType::VREvent_DriverRequestedQuit:
       case ::vr::EVREventType::VREvent_Quit:
       case ::vr::EVREventType::VREvent_ProcessQuit:
       case ::vr::EVREventType::VREvent_QuitAcknowledged:
       case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
         mIsHmdPresent = false;
@@ -423,29 +426,27 @@ VRDisplayOpenVR::SubmitFrame(MacIOSurfac
 }
 
 #endif
 
 VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID,
                                        uint32_t aNumButtons, uint32_t aNumTriggers,
                                        uint32_t aNumAxes, const nsCString& aId)
   : VRControllerHost(VRDeviceType::OpenVR, aHand, aDisplayID)
-  , mTrigger(aNumTriggers)
-  , mAxisMove(aNumAxes)
   , mVibrateThread(nullptr)
   , mIsVibrateStopped(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
 
-  mAxisMove.SetLengthAndRetainStorage(aNumAxes);
-  mTrigger.SetLengthAndRetainStorage(aNumTriggers);
-  mControllerInfo.mControllerName = aId;
-  mControllerInfo.mNumButtons = aNumButtons;
-  mControllerInfo.mNumAxes = aNumAxes;
-  mControllerInfo.mNumHaptics = kNumOpenVRHaptcs;
+  VRControllerState& state = mControllerInfo.mControllerState;
+  strncpy(state.mControllerName, aId.BeginReading(), kVRControllerNameMaxLen);
+  state.mNumButtons = aNumButtons;
+  state.mNumAxes = aNumAxes;
+  state.mNumTriggers = aNumTriggers;
+  state.mNumHaptics = kNumOpenVRHaptcs;
 }
 
 VRControllerOpenVR::~VRControllerOpenVR()
 {
   ShutdownVibrateHapticThread();
   MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
 }
 
@@ -459,41 +460,41 @@ uint32_t
 VRControllerOpenVR::GetTrackedIndex()
 {
   return mTrackedIndex;
 }
 
 float
 VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
 {
-  return mAxisMove[aAxis];
+  return mControllerInfo.mControllerState.mAxisValue[aAxis];
 }
 
 void
 VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
 {
-  mAxisMove[aAxis] = aValue;
+  mControllerInfo.mControllerState.mAxisValue[aAxis] = aValue;
 }
 
 void
 VRControllerOpenVR::SetTrigger(uint32_t aButton, float aValue)
 {
-  mTrigger[aButton] = aValue;
+  mControllerInfo.mControllerState.mTriggerValue[aButton] = aValue;
 }
 
 float
 VRControllerOpenVR::GetTrigger(uint32_t aButton)
 {
-  return mTrigger[aButton];
+  return mControllerInfo.mControllerState.mTriggerValue[aButton];
 }
 
 void
 VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
 {
-  mControllerInfo.mHand = aHand;
+  mControllerInfo.mControllerState.mHand = aHand;
 }
 
 void
 VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
                                         uint32_t aHapticIndex,
                                         double aIntensity,
                                         double aDuration,
                                         uint64_t aVibrateIndex,
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -107,18 +107,16 @@ private:
                            double aIntensity,
                            double aDuration,
                            uint64_t aVibrateIndex,
                            const VRManagerPromise& aPromise);
   void VibrateHapticComplete(const VRManagerPromise& aPromise);
 
   // The index of tracked devices from ::vr::IVRSystem.
   uint32_t mTrackedIndex;
-  nsTArray<float> mTrigger;
-  nsTArray<float> mAxisMove;
   RefPtr<VRThread> mVibrateThread;
   Atomic<bool> mIsVibrateStopped;
 };
 
 } // namespace impl
 
 class VRSystemManagerOpenVR : public VRSystemManager
 {
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -51,58 +51,60 @@ static const uint32_t kNumPuppetHaptcs =
 
 VRDisplayPuppet::VRDisplayPuppet()
  : VRDisplayHost(VRDeviceType::Puppet)
  , mIsPresenting(false)
  , mSensorState{}
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
 
-  mDisplayInfo.mDisplayName.AssignLiteral("Puppet HMD");
-  mDisplayInfo.mIsConnected = true;
-  mDisplayInfo.mIsMounted = false;
-  mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
-                                  VRDisplayCapabilityFlags::Cap_Orientation |
-                                  VRDisplayCapabilityFlags::Cap_Position |
-                                  VRDisplayCapabilityFlags::Cap_External |
-                                  VRDisplayCapabilityFlags::Cap_Present |
-                                  VRDisplayCapabilityFlags::Cap_StageParameters;
-  mDisplayInfo.mEyeResolution.width = 1836; // 1080 * 1.7
-  mDisplayInfo.mEyeResolution.height = 2040; // 1200 * 1.7
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
+  strncpy(state.mDisplayName, "Puppet HMD", kVRDisplayNameMaxLen);
+  state.mIsConnected = true;
+  state.mIsMounted = false;
+  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
+                           VRDisplayCapabilityFlags::Cap_Orientation |
+                           VRDisplayCapabilityFlags::Cap_Position |
+                           VRDisplayCapabilityFlags::Cap_External |
+                           VRDisplayCapabilityFlags::Cap_Present |
+                           VRDisplayCapabilityFlags::Cap_StageParameters;
+  state.mEyeResolution.width = 1836; // 1080 * 1.7
+  state.mEyeResolution.height = 2040; // 1200 * 1.7
 
   // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
   for (uint32_t eye = 0; eye < 2; ++eye) {
-    mDisplayInfo.mEyeTranslation[eye].x = 0.0f;
-    mDisplayInfo.mEyeTranslation[eye].y = 0.0f;
-    mDisplayInfo.mEyeTranslation[eye].z = 0.0f;
-    mDisplayInfo.mEyeFOV[eye] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
+    state.mEyeTranslation[eye].x = 0.0f;
+    state.mEyeTranslation[eye].y = 0.0f;
+    state.mEyeTranslation[eye].z = 0.0f;
+    state.mEyeFOV[eye] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
   }
 
   // default: 1m x 1m space, 0.75m high in seated position
-  mDisplayInfo.mStageSize.width = 1.0f;
-  mDisplayInfo.mStageSize.height = 1.0f;
+  state.mStageSize.width = 1.0f;
+  state.mStageSize.height = 1.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
-  mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
+  state.mSittingToStandingTransform[0] = 1.0f;
+  state.mSittingToStandingTransform[1] = 0.0f;
+  state.mSittingToStandingTransform[2] = 0.0f;
+  state.mSittingToStandingTransform[3] = 0.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
-  mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
+  state.mSittingToStandingTransform[4] = 0.0f;
+  state.mSittingToStandingTransform[5] = 1.0f;
+  state.mSittingToStandingTransform[6] = 0.0f;
+  state.mSittingToStandingTransform[7] = 0.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
-  mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
+  state.mSittingToStandingTransform[8] = 0.0f;
+  state.mSittingToStandingTransform[9] = 0.0f;
+  state.mSittingToStandingTransform[10] = 1.0f;
+  state.mSittingToStandingTransform[11] = 0.0f;
 
-  mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
-  mDisplayInfo.mSittingToStandingTransform._42 = 0.75f;
-  mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
+  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;
@@ -123,22 +125,23 @@ VRDisplayPuppet::~VRDisplayPuppet()
 {
   MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayHost);
 }
 
 void
 VRDisplayPuppet::SetDisplayInfo(const VRDisplayInfo& aDisplayInfo)
 {
   // We are only interested in the eye and mount info of the display info.
-  mDisplayInfo.mEyeResolution = aDisplayInfo.mEyeResolution;
-  mDisplayInfo.mIsMounted = aDisplayInfo.mIsMounted;
-  memcpy(&mDisplayInfo.mEyeFOV, &aDisplayInfo.mEyeFOV,
-         sizeof(mDisplayInfo.mEyeFOV[0]) * VRDisplayInfo::NumEyes);
-  memcpy(&mDisplayInfo.mEyeTranslation, &aDisplayInfo.mEyeTranslation,
-         sizeof(mDisplayInfo.mEyeTranslation[0]) * VRDisplayInfo::NumEyes);
+  VRDisplayState& state = mDisplayInfo.mDisplayState;
+  state.mEyeResolution = aDisplayInfo.mDisplayState.mEyeResolution;
+  state.mIsMounted = aDisplayInfo.mDisplayState.mIsMounted;
+  memcpy(&state.mEyeFOV, &aDisplayInfo.mDisplayState.mEyeFOV,
+         sizeof(state.mEyeFOV[0]) * VRDisplayState::NumEyes);
+  memcpy(&state.mEyeTranslation, &aDisplayInfo.mDisplayState.mEyeTranslation,
+         sizeof(state.mEyeTranslation[0]) * VRDisplayState::NumEyes);
 }
 
 void
 VRDisplayPuppet::Destroy()
 {
   StopPresentation();
 }
 
@@ -149,17 +152,17 @@ VRDisplayPuppet::ZeroSensor()
 
 VRHMDSensorState
 VRDisplayPuppet::GetSensorState()
 {
   mSensorState.inputFrameID = mDisplayInfo.mFrameId;
 
   Matrix4x4 matHeadToEye[2];
   for (uint32_t eye = 0; eye < 2; ++eye) {
-    matHeadToEye[eye].PreTranslate(mDisplayInfo.mEyeTranslation[eye]);
+    matHeadToEye[eye].PreTranslate(mDisplayInfo.GetEyeTranslation(eye));
   }
   mSensorState.CalcViewMatrices(matHeadToEye);
 
   return mSensorState;
 }
 
 void
 VRDisplayPuppet::SetSensorState(const VRHMDSensorState& aSensorState)
@@ -567,29 +570,30 @@ VRDisplayPuppet::SubmitFrame(const mozil
 }
 
 #endif
 
 void
 VRDisplayPuppet::Refresh()
 {
   // We update mIsConneced once per refresh.
-  mDisplayInfo.mIsConnected = true;
+  mDisplayInfo.mDisplayState.mIsConnected = true;
 }
 
 VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand, uint32_t aDisplayID)
   : VRControllerHost(VRDeviceType::Puppet, aHand, aDisplayID)
   , mButtonPressState(0)
   , mButtonTouchState(0)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerPuppet, VRControllerHost);
-  mControllerInfo.mControllerName.AssignLiteral("Puppet Gamepad");
-  mControllerInfo.mNumButtons = kNumPuppetButtonMask;
-  mControllerInfo.mNumAxes = kNumPuppetAxis;
-  mControllerInfo.mNumHaptics = kNumPuppetHaptcs;
+  VRControllerState& state = mControllerInfo.mControllerState;
+  strncpy(state.mControllerName, "Puppet Gamepad", kVRControllerNameMaxLen);
+  state.mNumButtons = kNumPuppetButtonMask;
+  state.mNumAxes = kNumPuppetAxis;
+  state.mNumHaptics = kNumPuppetHaptcs;
 }
 
 VRControllerPuppet::~VRControllerPuppet()
 {
   MOZ_COUNT_DTOR_INHERITED(VRControllerPuppet, VRControllerHost);
 }
 
 void
@@ -663,23 +667,23 @@ const dom::GamepadPoseState&
 VRControllerPuppet::GetPoseMoveState()
 {
   return mPoseState;
 }
 
 float
 VRControllerPuppet::GetAxisMove(uint32_t aAxis)
 {
-  return mAxisMove[aAxis];
+  return mControllerInfo.mControllerState.mAxisValue[aAxis];
 }
 
 void
 VRControllerPuppet::SetAxisMove(uint32_t aAxis, float aValue)
 {
-  mAxisMove[aAxis] = aValue;
+  mControllerInfo.mControllerState.mAxisValue[aAxis] = aValue;
 }
 
 VRSystemManagerPuppet::VRSystemManagerPuppet()
   : mPuppetDisplayCount(0)
   , mPuppetDisplayInfo{}
   , mPuppetDisplaySensorState{}
 {
 }
--- a/gfx/vr/gfxVRPuppet.h
+++ b/gfx/vr/gfxVRPuppet.h
@@ -93,17 +93,16 @@ public:
 
 protected:
   virtual ~VRControllerPuppet();
 
 private:
   uint64_t mButtonPressState;
   uint64_t mButtonTouchState;
   float mAxisMoveState[3];
-  float mAxisMove[3];
   dom::GamepadPoseState mPoseState;
 };
 
 } // namespace impl
 
 class VRSystemManagerPuppet : public VRSystemManager
 {
 public:
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -22,67 +22,111 @@ struct ParamTraits<mozilla::gfx::VRDevic
                                   mozilla::gfx::VRDeviceType(mozilla::gfx::VRDeviceType::NumVRDeviceTypes)> {};
 
 template<>
 struct ParamTraits<mozilla::gfx::VRDisplayCapabilityFlags> :
   public BitFlagsEnumSerializer<mozilla::gfx::VRDisplayCapabilityFlags,
                                 mozilla::gfx::VRDisplayCapabilityFlags::Cap_All> {};
 
 template <>
+struct ParamTraits<mozilla::gfx::VRDisplayState>
+{
+  typedef mozilla::gfx::VRDisplayState paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    // TODO - VRDisplayState is asserted to be a POD type
+    //        A simple memcpy may be sufficient here, or
+    //        this code can be refactored out if we use
+    //        shmem between the VR and content process.
+    nsCString displayName;
+    displayName.Assign(aParam.mDisplayName); // FINDME!! HACK! - Bounds checking?
+    WriteParam(aMsg, displayName);
+    WriteParam(aMsg, aParam.mCapabilityFlags);
+    WriteParam(aMsg, aParam.mEyeResolution.width);
+    WriteParam(aMsg, aParam.mEyeResolution.height);
+    WriteParam(aMsg, aParam.mIsConnected);
+    WriteParam(aMsg, aParam.mIsMounted);
+    WriteParam(aMsg, aParam.mStageSize.width);
+    WriteParam(aMsg, aParam.mStageSize.height);
+    for (int i = 0; i < 16; i++) {
+      // TODO - Should probably memcpy the whole array or
+      // convert Maxtrix4x4 to a POD type and use it
+      // instead
+      WriteParam(aMsg, aParam.mSittingToStandingTransform[i]);
+    }
+    for (int i = 0; i < mozilla::gfx::VRDisplayState::NumEyes; i++) {
+      WriteParam(aMsg, aParam.mEyeFOV[i]);
+      WriteParam(aMsg, aParam.mEyeTranslation[i].x);
+      WriteParam(aMsg, aParam.mEyeTranslation[i].y);
+      WriteParam(aMsg, aParam.mEyeTranslation[i].z);
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    nsCString displayName;
+    if (!ReadParam(aMsg, aIter, &(displayName)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mCapabilityFlags)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mEyeResolution.width)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mEyeResolution.height)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mIsConnected)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mIsMounted)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mStageSize.width)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mStageSize.height))) {
+      return false;
+    }
+    for (int i = 0; i < 16; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mSittingToStandingTransform[i]))) {
+        return false;
+      }
+    }
+    strncpy(aResult->mDisplayName, displayName.BeginReading(), mozilla::gfx::kVRDisplayNameMaxLen); // FINDME! TODO! HACK!  Safe? Better way?
+    for (int i = 0; i < mozilla::gfx::VRDisplayState::NumEyes; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mEyeFOV[i])) ||
+          !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i].x)) ||
+          !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i].y)) ||
+          !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i].z))) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
+template <>
 struct ParamTraits<mozilla::gfx::VRDisplayInfo>
 {
   typedef mozilla::gfx::VRDisplayInfo paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mType);
     WriteParam(aMsg, aParam.mDisplayID);
-    WriteParam(aMsg, aParam.mDisplayName);
-    WriteParam(aMsg, aParam.mCapabilityFlags);
-    WriteParam(aMsg, aParam.mEyeResolution);
-    WriteParam(aMsg, aParam.mIsConnected);
-    WriteParam(aMsg, aParam.mIsMounted);
     WriteParam(aMsg, aParam.mPresentingGroups);
     WriteParam(aMsg, aParam.mGroupMask);
-    WriteParam(aMsg, aParam.mStageSize);
-    WriteParam(aMsg, aParam.mSittingToStandingTransform);
     WriteParam(aMsg, aParam.mFrameId);
     WriteParam(aMsg, aParam.mPresentingGeneration);
-    for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
-      WriteParam(aMsg, aParam.mEyeFOV[i]);
-      WriteParam(aMsg, aParam.mEyeTranslation[i]);
-    }
+    WriteParam(aMsg, aParam.mDisplayState);
     for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
       WriteParam(aMsg, aParam.mLastSensorState[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->mDisplayName)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mCapabilityFlags)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mEyeResolution)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIsConnected)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mIsMounted)) ||
         !ReadParam(aMsg, aIter, &(aResult->mPresentingGroups)) ||
         !ReadParam(aMsg, aIter, &(aResult->mGroupMask)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mStageSize)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mSittingToStandingTransform)) ||
         !ReadParam(aMsg, aIter, &(aResult->mFrameId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mPresentingGeneration))) {
+        !ReadParam(aMsg, aIter, &(aResult->mPresentingGeneration)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mDisplayState))) {
       return false;
     }
-    for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
-      if (!ReadParam(aMsg, aIter, &(aResult->mEyeFOV[i])) ||
-          !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i]))) {
-        return false;
-      }
-    }
     for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mLastSensorState[i]))) {
         return false;
       }
     }
 
     return true;
   }
@@ -186,39 +230,88 @@ struct ParamTraits<mozilla::gfx::VRField
         !ReadParam(aMsg, aIter, &(aResult->leftDegrees))) {
       return false;
     }
 
     return true;
   }
 };
 
+
+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?
+    WriteParam(aMsg, controllerName);
+    WriteParam(aMsg, aParam.mNumButtons);
+    WriteParam(aMsg, aParam.mNumAxes);
+    WriteParam(aMsg, aParam.mNumTriggers);
+    WriteParam(aMsg, aParam.mNumHaptics);
+    WriteParam(aMsg, aParam.mButtonPressed);
+    WriteParam(aMsg, aParam.mButtonTouched);
+    for (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
+      WriteParam(aMsg, aParam.mAxisValue[i]);
+    }
+    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
+      WriteParam(aMsg, aParam.mTriggerValue[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))) {
+      return false;
+    }
+    for (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mAxisValue[i]))) {
+        return false;
+      }
+    }
+    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mTriggerValue[i]))) {
+        return false;
+      }
+    }
+    strncpy(aResult->mControllerName, controllerName.BeginReading(), mozilla::gfx::kVRControllerNameMaxLen); // FINDME! TODO! HACK!  Safe? Better way?
+
+    return true;
+  }
+};
+
 template <>
 struct ParamTraits<mozilla::gfx::VRControllerInfo>
 {
   typedef mozilla::gfx::VRControllerInfo paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mType);
     WriteParam(aMsg, aParam.mControllerID);
-    WriteParam(aMsg, aParam.mControllerName);
     WriteParam(aMsg, aParam.mMappingType);
-    WriteParam(aMsg, aParam.mNumButtons);
-    WriteParam(aMsg, aParam.mNumAxes);
+    WriteParam(aMsg, aParam.mControllerState);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->mType)) ||
         !ReadParam(aMsg, aIter, &(aResult->mControllerID)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mControllerName)) ||
         !ReadParam(aMsg, aIter, &(aResult->mMappingType)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumButtons)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumAxes))) {
+        !ReadParam(aMsg, aIter, &(aResult->mControllerState))) {
       return false;
     }
 
     return true;
   }
 };
 
 template <>
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
+    'external_api/moz_external_vr.h',
     'gfxVR.h',
     'ipc/VRLayerChild.h',
     'ipc/VRManagerChild.h',
     'ipc/VRManagerParent.h',
     'ipc/VRMessageUtils.h',
     'VRDisplayClient.h',
     'VRDisplayPresentation.h',
     'VRManager.h',
@@ -34,16 +35,17 @@ UNIFIED_SOURCES += [
     'VRManager.cpp',
     'VRThread.cpp',
 ]
 
 # VRDisplayHost includes MacIOSurface.h which includes Mac headers
 # which define Size and Points types in the root namespace that
 # often conflict with our own types.
 SOURCES += [
+    'gfxVRExternal.cpp',
     'gfxVRPuppet.cpp',
     'VRDisplayHost.cpp',
 ]
 
 # Build OpenVR on Windows, Linux, and macOS desktop targets
 if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
     DIRS += [
         'openvr',
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5351,16 +5351,18 @@ pref("dom.vr.enabled", false);
 // Window.vrdisplayactivate event when the headset's sensors detect it
 // being worn.  This can result in WebVR content taking over the headset
 // when the user is using it outside the browser or inadvertent start of
 // presentation due to the high sensitivity of the proximity sensor in some
 // headsets, so it is off by default.
 pref("dom.vr.autoactivate.enabled", false);
 // The threshold value of trigger inputs for VR controllers
 pref("dom.vr.controller_trigger_threshold", "0.1");
+// Enable external XR API integrations
+pref("dom.vr.external.enabled", false);
 // Maximum number of milliseconds the browser will wait for content to call
 // VRDisplay.requestPresent after emitting vrdisplayactivate during VR
 // link traversal.  This prevents a long running event handler for
 // vrdisplayactivate from later calling VRDisplay.requestPresent, which would
 // result in a non-responsive browser in the VR headset.
 pref("dom.vr.navigation.timeout", 5000);
 // Oculus device
 #if defined(HAVE_64BIT_BUILD)