Bug 1436791 - Implement gfxVRExternal,r=rbarker
authorKearwood Gilbert <kearwood@kearwood.com>
Tue, 13 Mar 2018 17:09:54 -0700
changeset 408089 04ed9a706cfcbdcb7df186aa7e1bdfa3fbc90d6e
parent 408088 09fef9f36f4ecbb8c238d140192c42da7fea418c
child 408090 2afa73fb650aee5099ca169c5888067a62fc6c2f
push id33626
push useraiakab@mozilla.com
push dateWed, 14 Mar 2018 10:01:05 +0000
treeherdermozilla-central@80b4777a6421 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrbarker
bugs1436791
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1436791 - Implement gfxVRExternal,r=rbarker - 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
@@ -359,16 +359,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,463 @@
+/* -*- 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;
+  }
+
+#if defined(XP_MACOSX)
+
+  if (mShmemFD == 0) {
+    mShmemFD = shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
+  }
+  if (mShmemFD <= 0) {
+    mShmemFD = 0;
+    return;
+  }
+
+  struct stat sb;
+  fstat(mShmemFD, &sb);
+  off_t length = sb.st_size;
+  if (length < (off_t)sizeof(VRExternalShmem)) {
+    // TODO - Implement logging
+    CloseShmem();
+    return;
+  }
+
+  mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, mShmemFD, 0);
+  if (mExternalShmem == MAP_FAILED) {
+    // TODO - Implement logging
+    mExternalShmem = NULL;
+    CloseShmem();
+    return;
+  }
+
+#elif defined(XP_WIN)
+
+  if (mShmemFile == NULL) {
+    mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName);
+    if (mShmemFile == NULL) {
+      // TODO - Implement logging
+      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) {
+    // TODO - Implement logging
+    CloseShmem();
+    return;
+  }
+#endif
+  CheckForShutdown();
+}
+
+void
+VRSystemManagerExternal::CheckForShutdown()
+{
+  if (mExternalShmem) {
+    if (mExternalShmem->generationA == -1 && mExternalShmem->generationB == -1) {
+      Shutdown();
+    }
+  }
+}
+
+void
+VRSystemManagerExternal::CloseShmem()
+{
+#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
+}
+
+/*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) {
+    // TODO - Add locking here for non-x86 platforms
+    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);
+    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);
+    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
@@ -5339,16 +5339,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)