Bug 1182048 - Part 2: Implement e10s support for WebVR,r=vlad
authorKearwood (Kip) Gilbert <kgilbert@mozilla.com>
Thu, 17 Sep 2015 14:23:13 -0700
changeset 312977 03e3553c83beae1609ccf3415ec575568bef8c11
parent 312976 04b05c76db2eab3feb94dfe7f93c1cd445b095bf
child 312978 3769091b5a3edd3f86ccbe372d9f5c0215d14d3a
push id5703
push userraliiev@mozilla.com
push dateMon, 07 Mar 2016 14:18:41 +0000
treeherdermozilla-beta@31e373ad5b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvlad
bugs1182048
milestone46.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 1182048 - Part 2: Implement e10s support for WebVR,r=vlad
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsIDocument.h
dom/base/nsPIDOMWindow.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/vr/VRDevice.cpp
dom/vr/VRDevice.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/d3d11/CompositorD3D11.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/LayersMessages.ipdlh
gfx/thebes/gfxPlatform.cpp
gfx/vr/VRDeviceProxy.cpp
gfx/vr/VRDeviceProxy.h
gfx/vr/VRDeviceProxyOrientationFallBack.cpp
gfx/vr/VRDeviceProxyOrientationFallBack.h
gfx/vr/VRManager.cpp
gfx/vr/VRManager.h
gfx/vr/gfxVR.cpp
gfx/vr/gfxVR.h
gfx/vr/gfxVRCardboard.cpp
gfx/vr/gfxVRCardboard.h
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROculus.h
gfx/vr/gfxVROculus050.cpp
gfx/vr/gfxVROculus050.h
gfx/vr/ipc/PVRManager.ipdl
gfx/vr/ipc/VRManagerChild.cpp
gfx/vr/ipc/VRManagerChild.h
gfx/vr/ipc/VRManagerParent.cpp
gfx/vr/ipc/VRManagerParent.h
gfx/vr/ipc/VRMessageUtils.h
gfx/vr/moz.build
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsFrame.cpp
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -215,16 +215,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedResolveResults)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageAreaListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDevicesPromises)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
 
 void
 Navigator::Invalidate()
 {
@@ -355,16 +356,18 @@ Navigator::Invalidate()
     mMediaKeySystemAccessManager->Shutdown();
     mMediaKeySystemAccessManager = nullptr;
   }
 #endif
 
   if (mDeviceStorageAreaListener) {
     mDeviceStorageAreaListener = nullptr;
   }
+
+  mVRGetDevicesPromises.Clear();
 }
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigator
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetUserAgent(nsAString& aUserAgent)
@@ -1934,26 +1937,45 @@ Navigator::GetVRDevices(ErrorResult& aRv
   }
 
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
   RefPtr<Promise> p = Promise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
+  // We pass ourself to RefreshVRDevices, so NotifyVRDevicesUpdated will
+  // be called asynchronously, resolving the promises in mVRGetDevicesPromises.
+  if (!VRDevice::RefreshVRDevices(this)) {
+    p->MaybeReject(NS_ERROR_FAILURE);
+    return p.forget();
+  }
+
+  mVRGetDevicesPromises.AppendElement(p);
+  return p.forget();
+}
+
+void
+Navigator::NotifyVRDevicesUpdated()
+{
+  // Synchronize the VR devices and resolve the promises in
+  // mVRGetDevicesPromises
   nsGlobalWindow* win = static_cast<nsGlobalWindow*>(mWindow.get());
 
   nsTArray<RefPtr<VRDevice>> vrDevs;
-  if (!win->GetVRDevices(vrDevs)) {
-    p->MaybeReject(NS_ERROR_FAILURE);
+  if (win->UpdateVRDevices(vrDevs)) {
+    for (auto p: mVRGetDevicesPromises) {
+      p->MaybeResolve(vrDevs);
+    }
   } else {
-    p->MaybeResolve(vrDevs);
+    for (auto p: mVRGetDevicesPromises) {
+      p->MaybeReject(NS_ERROR_FAILURE);
+    }
   }
-
-  return p.forget();
+  mVRGetDevicesPromises.Clear();
 }
 
 //*****************************************************************************
 //    Navigator::nsIMozNavigatorNetwork
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetProperties(nsINetworkProperties** aProperties)
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -263,16 +263,17 @@ public:
 #endif
 #ifdef MOZ_B2G_RIL
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
 #endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDevices(ErrorResult& aRv);
+  void NotifyVRDevicesUpdated();
 #ifdef MOZ_B2G_FM
   FMRadio* GetMozFMRadio(ErrorResult& aRv);
 #endif
 #ifdef MOZ_B2G_BT
   bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
 #endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
@@ -409,14 +410,16 @@ private:
   RefPtr<Presentation> mPresentation;
 
   // Hashtable for saving cached objects DoResolve created, so we don't create
   // the object twice if asked for it twice, whether due to use of "delete" or
   // due to Xrays.  We could probably use a nsJSThingHashtable here, but then
   // we'd need to figure out exactly how to trace that, and that seems to be
   // rocket science.  :(
   nsInterfaceHashtable<nsStringHashKey, nsISupports> mCachedResolveResults;
+
+  nsTArray<RefPtr<Promise> > mVRGetDevicesPromises;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Navigator_h
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -11437,20 +11437,20 @@ nsresult nsDocument::RemoteFrameFullscre
 
 nsresult nsDocument::RemoteFrameFullscreenReverted()
 {
   RestorePreviousFullScreenState();
   return NS_OK;
 }
 
 static void
-ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
+ReleaseVRDeviceProxyRef(void *, nsIAtom*, void *aPropertyValue, void *)
 {
   if (aPropertyValue) {
-    static_cast<gfx::VRHMDInfo*>(aPropertyValue)->Release();
+    static_cast<gfx::VRDeviceProxy*>(aPropertyValue)->Release();
   }
 }
 
 bool
 nsDocument::FullscreenElementReadyCheck(Element* aElement,
                                         bool aWasCallerChrome)
 {
   NS_ASSERTION(aElement,
@@ -11759,19 +11759,19 @@ nsDocument::ApplyFullscreen(const Fullsc
   nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
 
   // If a document is already in fullscreen, then unlock the mouse pointer
   // before setting a new document to fullscreen
   UnlockPointer();
 
   // Process options -- in this case, just HMD
   if (aRequest.mVRHMDDevice) {
-    RefPtr<gfx::VRHMDInfo> hmdRef = aRequest.mVRHMDDevice;
+    RefPtr<gfx::VRDeviceProxy> hmdRef = aRequest.mVRHMDDevice;
     elem->SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
-                      ReleaseHMDInfoRef, true);
+                      ReleaseVRDeviceProxyRef, true);
   }
 
   // Set the full-screen element. This sets the full-screen style on the
   // element, and the full-screen-ancestor styles on ancestors of the element
   // in this document.
   DebugOnly<bool> x = FullScreenStackPush(elem);
   NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
   // Set the iframe fullscreen flag.
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -106,17 +106,17 @@ struct FullscreenRequest : public Linked
   Element* GetElement() const { return mElement; }
   nsDocument* GetDocument() const { return mDocument; }
 
 private:
   RefPtr<Element> mElement;
   RefPtr<nsDocument> mDocument;
 
 public:
-  RefPtr<gfx::VRHMDInfo> mVRHMDDevice;
+  RefPtr<gfx::VRDeviceProxy> mVRHMDDevice;
   // This value should be true if the fullscreen request is
   // originated from chrome code.
   bool mIsCallerChrome = false;
   // This value denotes whether we should trigger a NewOrigin event if
   // requesting fullscreen in its document causes the origin which is
   // fullscreen to change. We may want *not* to trigger that event if
   // we're calling RequestFullScreen() as part of a continuation of a
   // request in a subdocument in different process, whereupon the caller
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1172,18 +1172,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
     mCleanedUp(false),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
-    mCanSkipCCGeneration(0),
-    mVRDevicesInitialized(false)
+    mCanSkipCCGeneration(0)
 {
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
   // Initialize the PRCList (this).
   PR_INIT_CLIST(this);
 
@@ -5959,17 +5958,17 @@ FullscreenTransitionTask::Observer::Obse
     obs->RemoveObserver(this, kPaintedTopic);
     mTask->mTimer = nullptr;
     mTask->Run();
   }
   return NS_OK;
 }
 
 static bool
-MakeWidgetFullscreen(nsGlobalWindow* aWindow, gfx::VRHMDInfo* aHMD,
+MakeWidgetFullscreen(nsGlobalWindow* aWindow, gfx::VRDeviceProxy* aHMD,
                      nsPIDOMWindow::FullscreenReason aReason, bool aFullscreen)
 {
   nsCOMPtr<nsIWidget> widget = aWindow->GetMainWidget();
   if (!widget) {
     return false;
   }
 
   FullscreenTransitionDuration duration;
@@ -5992,17 +5991,17 @@ MakeWidgetFullscreen(nsGlobalWindow* aWi
     task->Run();
   }
   return true;
 }
 
 nsresult
 nsGlobalWindow::SetFullscreenInternal(FullscreenReason aReason,
                                       bool aFullScreen,
-                                      gfx::VRHMDInfo* aHMD)
+                                      gfx::VRDeviceProxy* aHMD)
 {
   MOZ_ASSERT(IsOuterWindow());
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
              "Requires safe to run script as it "
              "may call FinishDOMFullscreenChange");
 
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
 
@@ -12936,29 +12935,21 @@ nsGlobalWindow::SyncGamepadState()
     for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
       gamepadsvc->SyncGamepadState(iter.Key(), iter.UserData());
     }
   }
 }
 #endif // MOZ_GAMEPAD
 
 bool
-nsGlobalWindow::GetVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices)
-{
-  FORWARD_TO_INNER(GetVRDevices, (aDevices), false);
-
-  if (!mVRDevicesInitialized) {
-    bool ok = mozilla::dom::VRDevice::CreateAllKnownVRDevices(ToSupports(this), mVRDevices);
-    if (!ok) {
-      mVRDevices.Clear();
-      return false;
-    }
-  }
-
-  mVRDevicesInitialized = true;
+nsGlobalWindow::UpdateVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices)
+{
+  FORWARD_TO_INNER(UpdateVRDevices, (aDevices), false);
+
+  VRDevice::UpdateVRDevices(mVRDevices, ToSupports(this));
   aDevices = mVRDevices;
   return true;
 }
 
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -124,17 +124,17 @@ class WindowOrientationObserver;
 namespace cache {
 class CacheStorage;
 } // namespace cache
 namespace indexedDB {
 class IDBFactory;
 } // namespace indexedDB
 } // namespace dom
 namespace gfx {
-class VRHMDInfo;
+class VRDeviceProxy;
 } // namespace gfx
 } // namespace mozilla
 
 extern already_AddRefed<nsIScriptTimeoutHandler>
 NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
                           mozilla::dom::Function& aFunction,
                           const mozilla::dom::Sequence<JS::Value>& aArguments,
                           mozilla::ErrorResult& aError);
@@ -466,17 +466,17 @@ public:
   virtual void RefreshCompartmentPrincipal() override;
 
   // For accessing protected field mFullScreen
   friend class FullscreenTransitionTask;
 
   // Outer windows only.
   virtual nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen,
-    mozilla::gfx::VRHMDInfo *aHMD = nullptr) override final;
+    mozilla::gfx::VRDeviceProxy *aHMD = nullptr) override final;
   virtual void FinishFullscreenChange(bool aIsFullscreen) override final;
   void SetWidgetFullscreen(FullscreenReason aReason, bool aIsFullscreen,
                            nsIWidget* aWidget, nsIScreen* aScreen);
   bool FullScreen() const;
 
   // Inner windows only.
   virtual void SetHasGamepadEventListener(bool aHasGamepad = true) override;
 
@@ -755,18 +755,18 @@ public:
   void SyncGamepadState();
 #endif
 
   // Inner windows only.
   // Enable/disable updates for gamepad input.
   void EnableGamepadUpdates();
   void DisableGamepadUpdates();
 
-  // Get the VR devices for this window, initializing if necessary
-  bool GetVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices);
+  // Update the VR devices for this window
+  bool UpdateVRDevices(nsTArray<RefPtr<mozilla::dom::VRDevice>>& aDevices);
 
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
     mozilla::EventListenerManager* elm = GetExistingListenerManager();        \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())    \
                : nullptr;                                                     \
   }                                                                           \
@@ -1843,22 +1843,18 @@ protected:
 #ifdef MOZ_WEBSPEECH
   // mSpeechSynthesis is only used on inner windows.
   RefPtr<mozilla::dom::SpeechSynthesis> mSpeechSynthesis;
 #endif
 
   // This is the CC generation the last time we called CanSkip.
   uint32_t mCanSkipCCGeneration;
 
-  // Did VR get initialized for this window?
-  bool                                       mVRDevicesInitialized;
   // The VRDevies for this window
   nsTArray<RefPtr<mozilla::dom::VRDevice>> mVRDevices;
-  // Any attached HMD when fullscreen
-  RefPtr<mozilla::gfx::VRHMDInfo>          mVRHMDInfo;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -97,20 +97,16 @@ class SVGAttrAnimationRuleProcessor;
 template<typename> class OwningNonNull;
 
 namespace css {
 class Loader;
 class ImageLoader;
 class Rule;
 } // namespace css
 
-namespace gfx {
-class VRHMDInfo;
-} // namespace gfx
-
 namespace dom {
 class AnonymousContent;
 class Attr;
 class BoxObject;
 class CDATASection;
 class Comment;
 struct CustomElementDefinition;
 class DocumentFragment;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -36,17 +36,17 @@ struct nsTimeout;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
 class ServiceWorkerRegistrationMainThread;
 } // namespace dom
 namespace gfx {
-class VRHMDInfo;
+class VRDeviceProxy;
 } // namespace gfx
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
 // all situations. Pushing popup state onto the stack never makes the
 // current popup state less permissive (see
 // nsGlobalWindow::PushPopupControlState()).
@@ -506,17 +506,17 @@ public:
    *
    * If aHMD is not null, the window is made full screen on the given VR HMD
    * device instead of its currrent display.
    *
    * Outer windows only.
    */
   virtual nsresult SetFullscreenInternal(
     FullscreenReason aReason, bool aIsFullscreen,
-    mozilla::gfx::VRHMDInfo *aHMD = nullptr) = 0;
+    mozilla::gfx::VRDeviceProxy *aHMD = nullptr) = 0;
 
   /**
    * This function should be called when the fullscreen state is flipped.
    * If no widget is involved the fullscreen change, this method is called
    * by SetFullscreenInternal, otherwise, it is called when the widget
    * finishes its change to or from fullscreen.
    *
    * @param aIsFullscreen indicates whether the widget is in fullscreen.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -194,16 +194,17 @@
 #include "mozilla/dom/voicemail/VoicemailIPCService.h"
 #include "mozilla/net/NeckoMessageUtils.h"
 #include "mozilla/widget/PuppetBidiKeyboard.h"
 #include "mozilla/RemoteSpellCheckEngineChild.h"
 #include "GMPServiceChild.h"
 #include "GMPDecoderModule.h"
 #include "gfxPlatform.h"
 #include "nscore.h" // for NS_FREE_PERMANENT_DATA
+#include "VRManagerChild.h"
 
 using namespace mozilla;
 using namespace mozilla::docshell;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::icc;
 using namespace mozilla::dom::ipc;
@@ -1255,16 +1256,23 @@ ContentChild::AllocPSharedBufferManagerC
 
 PImageBridgeChild*
 ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                                      base::ProcessId aOtherProcess)
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
+gfx::PVRManagerChild*
+ContentChild::AllocPVRManagerChild(Transport* aTransport,
+                                   ProcessId aOtherProcess)
+{
+  return gfx::VRManagerChild::StartUpInChildProcess(aTransport, aOtherProcess);
+}
+
 PBackgroundChild*
 ContentChild::AllocPBackgroundChild(Transport* aTransport,
                                     ProcessId aOtherProcess)
 {
     return BackgroundChild::Alloc(aTransport, aOtherProcess);
 }
 
 PProcessHangMonitorChild*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -143,16 +143,20 @@ public:
     PImageBridgeChild*
     AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport,
                            base::ProcessId aOtherProcess) override;
 
     PProcessHangMonitorChild*
     AllocPProcessHangMonitorChild(Transport* aTransport,
                                   ProcessId aOtherProcess) override;
 
+    PVRManagerChild*
+    AllocPVRManagerChild(Transport* aTransport,
+                         ProcessId aOtherProcess) override;
+
     virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
 
     PBackgroundChild*
     AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
                           override;
 
     virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
                                               const IPCTabContext& aContext,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -263,16 +263,18 @@ using namespace mozilla::system;
 #include "nsIProfiler.h"
 #include "nsIProfileSaveEvent.h"
 #endif
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadMonitoring.h"
 #endif
 
+#include "VRManagerParent.h"            // for VRManagerParent
+
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 #if defined(XP_WIN)
 // e10s forced enable pref, defined in nsAppRunner.cpp
 extern const char* kForceEnableE10sPref;
 #endif
 
 using base::ChildPrivileges;
@@ -2634,16 +2636,19 @@ ContentParent::InitInternal(ProcessPrior
         // on demand.)
         bool useOffMainThreadCompositing = !!CompositorParent::CompositorLoop();
         if (useOffMainThreadCompositing) {
             DebugOnly<bool> opened = PCompositor::Open(this);
             MOZ_ASSERT(opened);
 
             opened = PImageBridge::Open(this);
             MOZ_ASSERT(opened);
+
+            opened = gfx::PVRManager::Open(this);
+            MOZ_ASSERT(opened);
         }
 #ifdef MOZ_WIDGET_GONK
         DebugOnly<bool> opened = PSharedBufferManager::Open(this);
         MOZ_ASSERT(opened);
 #endif
     }
 
     if (gAppData) {
@@ -3406,16 +3411,23 @@ ContentParent::AllocPGMPServiceParent(mo
 
 PCompositorParent*
 ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
                                       base::ProcessId aOtherProcess)
 {
     return CompositorParent::Create(aTransport, aOtherProcess);
 }
 
+gfx::PVRManagerParent*
+ContentParent::AllocPVRManagerParent(Transport* aTransport,
+                                     ProcessId aOtherProcess)
+{
+  return gfx::VRManagerParent::CreateCrossProcess(aTransport, aOtherProcess);
+}
+
 PImageBridgeParent*
 ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
                                        base::ProcessId aOtherProcess)
 {
     return ImageBridgeParent::Create(aTransport, aOtherProcess);
 }
 
 PBackgroundParent*
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -607,16 +607,20 @@ private:
     PBackgroundParent*
     AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
                            override;
 
     PProcessHangMonitorParent*
     AllocPProcessHangMonitorParent(Transport* aTransport,
                                    ProcessId aOtherProcess) override;
 
+    PVRManagerParent*
+    AllocPVRManagerParent(Transport* aTransport,
+                          ProcessId aOtherProcess) override;
+
     virtual bool RecvGetProcessAttributes(ContentParentId* aCpId,
                                           bool* aIsForApp,
                                           bool* aIsForBrowser) override;
     virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
                                                bool* aIsConnected,
                                                bool* aIsLangRTL,
                                                InfallibleTArray<nsString>* dictionaries,
                                                ClipboardCapabilities* clipboardCaps,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -49,16 +49,17 @@ include protocol PStorage;
 include protocol PTelephony;
 include protocol PTestShell;
 include protocol PVoicemail;
 include protocol PJavaScript;
 include protocol PRemoteSpellcheckEngine;
 include protocol PWebBrowserPersistDocument;
 include protocol PWebrtcGlobal;
 include protocol PPresentation;
+include protocol PVRManager;
 include DOMTypes;
 include JavaScriptTypes;
 include InputStreamParams;
 include PTabContext;
 include URIParams;
 include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
@@ -446,16 +447,17 @@ prio(normal upto urgent) sync protocol P
 {
     parent spawns PPluginModule;
 
     parent opens PCompositor;
     parent opens PProcessHangMonitor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     parent opens PGMPService;
+    parent opens PVRManager;
     child opens PBackground;
 
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
     manages PCrashReporter;
--- a/dom/vr/VRDevice.cpp
+++ b/dom/vr/VRDevice.cpp
@@ -5,24 +5,67 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWrapperCache.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/VRDeviceBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/VRDevice.h"
+#include "Navigator.h"
 #include "gfxVR.h"
+#include "VRDeviceProxy.h"
+#include "VRManagerChild.h"
 #include "nsIFrame.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
+/*static*/ bool
+VRDevice::RefreshVRDevices(dom::Navigator* aNavigator)
+{
+  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
+  return vm && vm->RefreshVRDevicesWithCallback(aNavigator);
+}
+
+/*static*/ void
+VRDevice::UpdateVRDevices(nsTArray<RefPtr<VRDevice>>& aDevices, nsISupports* aParent)
+{
+  nsTArray<RefPtr<VRDevice>> devices;
+
+  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
+  nsTArray<RefPtr<gfx::VRDeviceProxy>> proxyDevices;
+  if (vm && vm->GetVRDevices(proxyDevices)) {
+    for (size_t i = 0; i < proxyDevices.Length(); i++) {
+      RefPtr<gfx::VRDeviceProxy> proxyDevice = proxyDevices[i];
+      bool isNewDevice = true;
+      for (size_t j = 0; j < aDevices.Length(); j++) {
+        if (aDevices[j]->GetHMD()->GetDeviceInfo() == proxyDevice->GetDeviceInfo()) {
+          devices.AppendElement(aDevices[j]);
+          isNewDevice = false;
+        }
+      }
+
+      if (isNewDevice) {
+        gfx::VRStateValidFlags sensorBits = proxyDevice->GetDeviceInfo().GetSupportedSensorStateBits();
+        devices.AppendElement(new HMDInfoVRDevice(aParent, proxyDevice));
+        if (sensorBits & (gfx::VRStateValidFlags::State_Position |
+            gfx::VRStateValidFlags::State_Orientation))
+        {
+          devices.AppendElement(new HMDPositionVRDevice(aParent, proxyDevice));
+        }
+      }
+    }
+  }
+
+  aDevices = devices;
+}
+
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfViewReadOnly, mParent)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfViewReadOnly, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfViewReadOnly, Release)
 
 JSObject*
 VRFieldOfViewReadOnly::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VRFieldOfViewReadOnlyBinding::Wrap(aCx, this, aGivenProto);
@@ -128,21 +171,21 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRP
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRPositionState, Release)
 
 VRPositionState::VRPositionState(nsISupports* aParent, const gfx::VRHMDSensorState& aState)
   : mParent(aParent)
   , mVRState(aState)
 {
   mTimeStamp = aState.timestamp;
 
-  if (aState.flags & gfx::VRHMDInfo::State_Position) {
+  if (aState.flags & gfx::VRStateValidFlags::State_Position) {
     mPosition = new DOMPoint(mParent, aState.position[0], aState.position[1], aState.position[2], 0.0);
   }
 
-  if (aState.flags & gfx::VRHMDInfo::State_Orientation) {
+  if (aState.flags & gfx::VRStateValidFlags::State_Orientation) {
     mOrientation = new DOMPoint(mParent, aState.orientation[0], aState.orientation[1], aState.orientation[2], aState.orientation[3]);
   }
 }
 
 DOMPoint*
 VRPositionState::GetLinearVelocity()
 {
   if (!mLinearVelocity) {
@@ -201,164 +244,124 @@ HMDVRDevice::WrapObject(JSContext* aCx, 
 }
 
 /* virtual */ JSObject*
 PositionSensorVRDevice::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PositionSensorVRDeviceBinding::Wrap(aCx, this, aGivenProto);
 }
 
-namespace {
-
-class HMDInfoVRDevice : public HMDVRDevice
+HMDInfoVRDevice::HMDInfoVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD)
+  : HMDVRDevice(aParent, aHMD)
 {
-public:
-  HMDInfoVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
-    : HMDVRDevice(aParent, aHMD)
-  {
-    uint64_t hmdid = aHMD->GetDeviceIndex() << 8;
-    uint64_t devid = hmdid | 0x00; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor
+  MOZ_COUNT_CTOR_INHERITED(HMDInfoVRDevice, HMDVRDevice);
+  uint64_t hmdid = aHMD->GetDeviceInfo().GetDeviceID() << 8;
+  uint64_t devid = hmdid | 0x00; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor
+
+  mHWID.Truncate();
+  mHWID.AppendPrintf("0x%llx", hmdid);
+
+  mDeviceId.Truncate();
+  mDeviceId.AppendPrintf("0x%llx", devid);
+
+  mDeviceName.Truncate();
+  mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceInfo().GetDeviceName()));
+  mDeviceName.AppendLiteral(" (HMD)");
+
+  mValid = true;
+}
 
-    mHWID.Truncate();
-    mHWID.AppendPrintf("0x%llx", hmdid);
-
-    mDeviceId.Truncate();
-    mDeviceId.AppendPrintf("0x%llx", devid);
+HMDInfoVRDevice::~HMDInfoVRDevice()
+{
+  MOZ_COUNT_DTOR_INHERITED(HMDInfoVRDevice, HMDVRDevice);
+}
 
-    mDeviceName.Truncate();
-    mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceName()));
-    mDeviceName.AppendLiteral(" (HMD)");
+/* If a field of view that is set to all 0's is passed in,
+ * the recommended field of view for that eye is used.
+ */
+void
+HMDInfoVRDevice::SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
+                                const VRFieldOfViewInit& aRightFOV,
+                                double zNear, double zFar)
+{
+  gfx::VRFieldOfView left = gfx::VRFieldOfView(aLeftFOV.mUpDegrees, aLeftFOV.mRightDegrees,
+                                               aLeftFOV.mDownDegrees, aLeftFOV.mLeftDegrees);
+  gfx::VRFieldOfView right = gfx::VRFieldOfView(aRightFOV.mUpDegrees, aRightFOV.mRightDegrees,
+                                                aRightFOV.mDownDegrees, aRightFOV.mLeftDegrees);
 
-    mValid = true;
+  if (left.IsZero()) {
+    left = mHMD->GetDeviceInfo().GetRecommendedEyeFOV(VRDeviceInfo::Eye_Left);
   }
 
-  virtual ~HMDInfoVRDevice() { }
-
-  /* If a field of view that is set to all 0's is passed in,
-   * the recommended field of view for that eye is used.
-   */
-  virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
-                              const VRFieldOfViewInit& aRightFOV,
-                              double zNear, double zFar) override
-  {
-    gfx::VRFieldOfView left = gfx::VRFieldOfView(aLeftFOV.mUpDegrees, aLeftFOV.mRightDegrees,
-                                                 aLeftFOV.mDownDegrees, aLeftFOV.mLeftDegrees);
-    gfx::VRFieldOfView right = gfx::VRFieldOfView(aRightFOV.mUpDegrees, aRightFOV.mRightDegrees,
-                                                  aRightFOV.mDownDegrees, aRightFOV.mLeftDegrees);
-
-    if (left.IsZero())
-      left = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Left);
-    if (right.IsZero())
-      right = mHMD->GetRecommendedEyeFOV(VRHMDInfo::Eye_Right);
-
-    mHMD->SetFOV(left, right, zNear, zFar);
-  }
-
-  virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) override
-  {
-    gfx::IntSize sz(mHMD->SuggestedEyeResolution());
-    gfx::VRHMDInfo::Eye eye = aEye == VREye::Left ? gfx::VRHMDInfo::Eye_Left : gfx::VRHMDInfo::Eye_Right;
-    RefPtr<VREyeParameters> params =
-      new VREyeParameters(mParent,
-                          gfx::VRFieldOfView(15, 15, 15, 15), // XXX min?
-                          mHMD->GetMaximumEyeFOV(eye),
-                          mHMD->GetRecommendedEyeFOV(eye),
-                          mHMD->GetEyeTranslation(eye),
-                          mHMD->GetEyeFOV(eye),
-                          gfx::IntRect((aEye == VREye::Left) ? 0 : sz.width, 0, sz.width, sz.height));
-    return params.forget();
+  if (right.IsZero()) {
+    right = mHMD->GetDeviceInfo().GetRecommendedEyeFOV(VRDeviceInfo::Eye_Right);
   }
 
-protected:
-};
-
-class HMDPositionVRDevice : public PositionSensorVRDevice
-{
-public:
-  HMDPositionVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
-    : PositionSensorVRDevice(aParent)
-    , mHMD(aHMD)
-    , mTracking(false)
-  {
-
-    uint64_t hmdid = aHMD->GetDeviceIndex() << 8;
-    uint64_t devid = hmdid | 0x01; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor
-
-    mHWID.Truncate();
-    mHWID.AppendPrintf("0x%llx", hmdid);
-
-    mDeviceId.Truncate();
-    mDeviceId.AppendPrintf("0x%llx", devid);
+  mHMD->SetFOV(left, right, zNear, zFar);
+}
 
-    mDeviceName.Truncate();
-    mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceName()));
-    mDeviceName.AppendLiteral(" (Sensor)");
-
-    mValid = true;
-  }
+already_AddRefed<VREyeParameters> HMDInfoVRDevice::GetEyeParameters(VREye aEye)
+{
+  gfx::IntSize sz(mHMD->GetDeviceInfo().SuggestedEyeResolution());
+  gfx::VRDeviceInfo::Eye eye = aEye == VREye::Left ? gfx::VRDeviceInfo::Eye_Left : gfx::VRDeviceInfo::Eye_Right;
+  RefPtr<VREyeParameters> params =
+    new VREyeParameters(mParent,
+                        gfx::VRFieldOfView(15, 15, 15, 15), // XXX min?
+                        mHMD->GetDeviceInfo().GetMaximumEyeFOV(eye),
+                        mHMD->GetDeviceInfo().GetRecommendedEyeFOV(eye),
+                        mHMD->GetDeviceInfo().GetEyeTranslation(eye),
+                        mHMD->GetDeviceInfo().GetEyeFOV(eye),
+                        gfx::IntRect((aEye == VREye::Left) ? 0 : sz.width, 0, sz.width, sz.height));
+  return params.forget();
+}
 
-  ~HMDPositionVRDevice()
-  {
-    if (mTracking) {
-      mHMD->StopSensorTracking();
-    }
-  }
+HMDPositionVRDevice::HMDPositionVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD)
+  : PositionSensorVRDevice(aParent, aHMD)
+{
+  MOZ_COUNT_CTOR_INHERITED(HMDPositionVRDevice, PositionSensorVRDevice);
 
-  virtual already_AddRefed<VRPositionState> GetState() override
-  {
-    if (!mTracking) {
-      mHMD->StartSensorTracking();
-      mTracking = true;
-    }
+  uint64_t hmdid = aHMD->GetDeviceInfo().GetDeviceID() << 8;
+  uint64_t devid = hmdid | 0x01; // we generate a devid with low byte 0 for the HMD, 1 for the position sensor
 
-    gfx::VRHMDSensorState state = mHMD->GetSensorState();
-    RefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
+  mHWID.Truncate();
+  mHWID.AppendPrintf("0x%llx", hmdid);
+
+  mDeviceId.Truncate();
+  mDeviceId.AppendPrintf("0x%llx", devid);
 
-    return obj.forget();
-  }
-
-  virtual already_AddRefed<VRPositionState> GetImmediateState() override
-  {
-    if (!mTracking) {
-      mHMD->StartSensorTracking();
-      mTracking = true;
-    }
+  mDeviceName.Truncate();
+  mDeviceName.Append(NS_ConvertASCIItoUTF16(aHMD->GetDeviceInfo().GetDeviceName()));
+  mDeviceName.AppendLiteral(" (Sensor)");
 
-    gfx::VRHMDSensorState state = mHMD->GetSensorState();
-    RefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
+  mValid = true;
+}
 
-    return obj.forget();
-  }
-
-  virtual void ResetSensor() override
-  {
-    mHMD->ZeroSensor();
-  }
+HMDPositionVRDevice::~HMDPositionVRDevice()
+{
+  MOZ_COUNT_DTOR_INHERITED(HMDPositionVRDevice, PositionSensorVRDevice);
+}
 
-protected:
-  RefPtr<gfx::VRHMDInfo> mHMD;
-  bool mTracking;
-};
+already_AddRefed<VRPositionState>
+HMDPositionVRDevice::GetState()
+{
+  gfx::VRHMDSensorState state = mHMD->GetSensorState();
+  RefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
 
-} // namespace
-
-bool
-VRDevice::CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<RefPtr<VRDevice>>& aDevices)
-{
-  nsTArray<RefPtr<gfx::VRHMDInfo>> hmds;
-  gfx::VRHMDManager::GetAllHMDs(hmds);
+  return obj.forget();
+}
 
-  for (size_t i = 0; i < hmds.Length(); ++i) {
-    uint32_t sensorBits = hmds[i]->GetSupportedSensorStateBits();
-    aDevices.AppendElement(new HMDInfoVRDevice(aParent, hmds[i]));
+already_AddRefed<VRPositionState>
+HMDPositionVRDevice::GetImmediateState()
+{
+  gfx::VRHMDSensorState state = mHMD->GetSensorState();
+  RefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
 
-    if (sensorBits &
-        (gfx::VRHMDInfo::State_Position | gfx::VRHMDInfo::State_Orientation))
-    {
-      aDevices.AppendElement(new HMDPositionVRDevice(aParent, hmds[i]));
-    }
-  }
+  return obj.forget();
+}
 
-  return true;
+void
+HMDPositionVRDevice::ResetSensor()
+{
+  mHMD->ZeroSensor();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/vr/VRDevice.h
+++ b/dom/vr/VRDevice.h
@@ -16,19 +16,21 @@
 #include "mozilla/dom/DOMRect.h"
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
 #include "gfxVR.h"
+#include "VRDeviceProxy.h"
 
 namespace mozilla {
 namespace dom {
+class Navigator;
 
 class VRFieldOfViewReadOnly : public nsWrapperCache
 {
 public:
   VRFieldOfViewReadOnly(nsISupports* aParent,
                         double aUpDegrees, double aRightDegrees,
                         double aDownDegrees, double aLeftDegrees)
     : mParent(aParent)
@@ -172,59 +174,68 @@ protected:
   RefPtr<VRFieldOfView> mCurFOV;
   RefPtr<DOMRect> mRenderRect;
 };
 
 class VRDevice : public nsISupports,
                  public nsWrapperCache
 {
 public:
-  // create new VRDevice objects for all known underlying gfx::vr devices
-  static bool CreateAllKnownVRDevices(nsISupports *aParent, nsTArray<RefPtr<VRDevice>>& aDevices);
 
-public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(VRDevice)
 
   void GetHardwareUnitId(nsAString& aHWID) const { aHWID = mHWID; }
   void GetDeviceId(nsAString& aDeviceId) const { aDeviceId = mDeviceId; }
   void GetDeviceName(nsAString& aDeviceName) const { aDeviceName = mDeviceName; }
 
   bool IsValid() { return mValid; }
 
-  virtual void Shutdown() { }
-
   nsISupports* GetParentObject() const
   {
     return mParent;
   }
 
   enum VRDeviceType {
     HMD,
     PositionSensor
   };
 
   VRDeviceType GetType() const { return mType; }
 
+  static bool RefreshVRDevices(dom::Navigator* aNavigator);
+  static void UpdateVRDevices(nsTArray<RefPtr<VRDevice> >& aDevices,
+                              nsISupports* aParent);
+
+  gfx::VRDeviceProxy *GetHMD() {
+    return mHMD;
+  }
+
 protected:
-  VRDevice(nsISupports* aParent, VRDeviceType aType)
+  VRDevice(nsISupports* aParent,
+           gfx::VRDeviceProxy* aHMD,
+           VRDeviceType aType)
     : mParent(aParent)
+    , mHMD(aHMD)
     , mType(aType)
     , mValid(false)
   {
+    MOZ_COUNT_CTOR(VRDevice);
     mHWID.AssignLiteral("uknown");
     mDeviceId.AssignLiteral("unknown");
     mDeviceName.AssignLiteral("unknown");
   }
 
-  virtual ~VRDevice() {
-    Shutdown();
+  virtual ~VRDevice()
+  {
+    MOZ_COUNT_DTOR(VRDevice);
   }
 
   nsCOMPtr<nsISupports> mParent;
+  RefPtr<gfx::VRDeviceProxy> mHMD;
   nsString mHWID;
   nsString mDeviceId;
   nsString mDeviceName;
 
   VRDeviceType mType;
 
   bool mValid;
 };
@@ -235,44 +246,72 @@ public:
   virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) = 0;
 
   virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
                               const VRFieldOfViewInit& aRightFOV,
                               double zNear, double zFar) = 0;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  gfx::VRHMDInfo *GetHMD() { return mHMD.get(); }
+protected:
+  HMDVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD)
+    : VRDevice(aParent, aHMD, VRDevice::HMD)
+  {
+    MOZ_COUNT_CTOR_INHERITED(HMDVRDevice, VRDevice);
+  }
+
+  virtual ~HMDVRDevice()
+  {
+    MOZ_COUNT_DTOR_INHERITED(HMDVRDevice, VRDevice);
+  }
+};
 
-protected:
-  HMDVRDevice(nsISupports* aParent, gfx::VRHMDInfo* aHMD)
-    : VRDevice(aParent, VRDevice::HMD)
-    , mHMD(aHMD)
-  { }
+class HMDInfoVRDevice : public HMDVRDevice
+{
+public:
+  HMDInfoVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD);
+  virtual ~HMDInfoVRDevice();
 
-  virtual ~HMDVRDevice() { }
-
-  RefPtr<gfx::VRHMDInfo> mHMD;
+  /* If a field of view that is set to all 0's is passed in,
+   * the recommended field of view for that eye is used.
+   */
+  virtual void SetFieldOfView(const VRFieldOfViewInit& aLeftFOV,
+                              const VRFieldOfViewInit& aRightFOV,
+                              double zNear, double zFar) override;
+  virtual already_AddRefed<VREyeParameters> GetEyeParameters(VREye aEye) override;
 };
 
 class PositionSensorVRDevice : public VRDevice
 {
 public:
   virtual already_AddRefed<VRPositionState> GetState() = 0;
-
   virtual already_AddRefed<VRPositionState> GetImmediateState() = 0;
-
   virtual void ResetSensor() = 0;
-
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
 protected:
-  explicit PositionSensorVRDevice(nsISupports* aParent)
-    : VRDevice(aParent, VRDevice::PositionSensor)
-  { }
+  explicit PositionSensorVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD)
+    : VRDevice(aParent, aHMD, VRDevice::PositionSensor)
+  {
+    MOZ_COUNT_CTOR_INHERITED(PositionSensorVRDevice, VRDevice);
+  }
 
-  virtual ~PositionSensorVRDevice() { }
+  virtual ~PositionSensorVRDevice()
+  {
+    MOZ_COUNT_DTOR_INHERITED(PositionSensorVRDevice, VRDevice);
+  }
+};
+
+class HMDPositionVRDevice : public PositionSensorVRDevice
+{
+public:
+  HMDPositionVRDevice(nsISupports* aParent, gfx::VRDeviceProxy* aHMD);
+  ~HMDPositionVRDevice();
+
+  virtual already_AddRefed<VRPositionState> GetState() override;
+  virtual already_AddRefed<VRPositionState> GetImmediateState() override;
+  virtual void ResetSensor() override;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1112,17 +1112,18 @@ ContainerLayer::ContainerLayer(LayerMana
     mInheritedXScale(1.0f),
     mInheritedYScale(1.0f),
     mPresShellResolution(1.0f),
     mScaleToResolution(false),
     mUseIntermediateSurface(false),
     mSupportsComponentAlphaChildren(false),
     mMayHaveReadbackChild(false),
     mChildrenChanged(false),
-    mEventRegionsOverride(EventRegionsOverride::NoOverride)
+    mEventRegionsOverride(EventRegionsOverride::NoOverride),
+    mVRDeviceID(0)
 {
   MOZ_COUNT_CTOR(ContainerLayer);
   mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
 }
 
 ContainerLayer::~ContainerLayer()
 {
   MOZ_COUNT_DTOR(ContainerLayer);
@@ -1274,17 +1275,17 @@ ContainerLayer::RepositionChild(Layer* a
 
 void
 ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
   aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
                                     mInheritedXScale, mInheritedYScale,
                                     mPresShellResolution, mScaleToResolution,
                                     mEventRegionsOverride,
-                                    reinterpret_cast<uint64_t>(mHMDInfo.get()));
+                                    mVRDeviceID);
 }
 
 bool
 ContainerLayer::Creates3DContextWithExtendingChildren()
 {
   if (Extend3DContext()) {
     return false;
   }
@@ -2138,18 +2139,18 @@ ContainerLayer::PrintInfo(std::stringstr
     aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get();
   }
   if (mEventRegionsOverride & EventRegionsOverride::ForceDispatchToContent) {
     aStream << " [force-dtc]";
   }
   if (mEventRegionsOverride & EventRegionsOverride::ForceEmptyHitRegion) {
     aStream << " [force-ehr]";
   }
-  if (mHMDInfo) {
-    aStream << nsPrintfCString(" [hmd=%p]", mHMDInfo.get()).get();
+  if (mVRDeviceID) {
+    aStream << nsPrintfCString(" [hmd=%lu]", mVRDeviceID).get();
   }
 }
 
 void
 ContainerLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
 {
   Layer::DumpPacket(aPacket, aParent);
   // Get this layer data
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -2124,18 +2124,22 @@ public:
 
   EventRegionsOverride GetEventRegionsOverride() const {
     return mEventRegionsOverride;
   }
 
   /**
    * VR
    */
-  void SetVRHMDInfo(gfx::VRHMDInfo* aHMD) { mHMDInfo = aHMD; }
-  gfx::VRHMDInfo* GetVRHMDInfo() { return mHMDInfo; }
+  void SetVRDeviceID(uint32_t aVRDeviceID) {
+    mVRDeviceID = aVRDeviceID;
+  }
+  uint32_t GetVRDeviceID() {
+    return mVRDeviceID;
+  }
 
   /**
    * Replace the current effective transform with the given one,
    * returning the old one.  This is currently added as a hack for VR
    * rendering, and might go away if we find a better way to do this.
    * If you think you have a need for this method, talk with
    * vlad/mstange/mwoodrow first.
    */
@@ -2201,17 +2205,17 @@ protected:
   bool mScaleToResolution;
   bool mUseIntermediateSurface;
   bool mSupportsComponentAlphaChildren;
   bool mMayHaveReadbackChild;
   // This is updated by ComputeDifferences. This will be true if we need to invalidate
   // the intermediate surface.
   bool mChildrenChanged;
   EventRegionsOverride mEventRegionsOverride;
-  RefPtr<gfx::VRHMDInfo> mHMDInfo;
+  uint32_t mVRDeviceID;
 };
 
 /**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
  * restrict the area filled, set a clip region on this layer.
  */
 class ColorLayer : public Layer {
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -27,16 +27,17 @@
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsISupportsUtils.h"           // for NS_ADDREF, NS_RELEASE
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsAutoTArray
 #include "TextRenderer.h"               // for TextRenderer
 #include <vector>
+#include "VRManager.h"                  // for VRManager
 #include "GeckoProfiler.h"              // for GeckoProfiler
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "ProfilerMarkers.h"            // for ProfilerMarkers
 #endif
 
 #define CULLING_LOG(...)
 // #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
 
@@ -133,28 +134,28 @@ struct PreparedLayer
   RenderTargetIntRect mClipRect;
 };
 
 
 template<class ContainerT> void
 ContainerRenderVR(ContainerT* aContainer,
                   LayerManagerComposite* aManager,
                   const gfx::IntRect& aClipRect,
-                  gfx::VRHMDInfo* aHMD)
+                  RefPtr<gfx::VRHMDInfo> aHMD)
 {
   RefPtr<CompositingRenderTarget> surface;
 
   Compositor* compositor = aManager->GetCompositor();
 
   RefPtr<CompositingRenderTarget> previousTarget = compositor->GetCurrentRenderTarget();
 
   float opacity = aContainer->GetEffectiveOpacity();
 
   // The size of each individual eye surface
-  gfx::IntSize eyeResolution = aHMD->SuggestedEyeResolution();
+  gfx::IntSize eyeResolution = aHMD->GetDeviceInfo().SuggestedEyeResolution();
   gfx::IntRect eyeRect[2];
   eyeRect[0] = gfx::IntRect(0, 0, eyeResolution.width, eyeResolution.height);
   eyeRect[1] = gfx::IntRect(eyeResolution.width, 0, eyeResolution.width, eyeResolution.height);
 
   // The intermediate surface size; we're going to assume that we're not going to run
   // into max texture size limits
   gfx::IntRect surfaceRect = gfx::IntRect(0, 0, eyeResolution.width * 2, eyeResolution.height);
 
@@ -343,17 +344,17 @@ struct PreparedData
 template<class ContainerT> void
 ContainerPrepare(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
                  const RenderTargetIntRect& aClipRect)
 {
   aContainer->mPrepared = MakeUnique<PreparedData>();
   aContainer->mPrepared->mNeedsSurfaceCopy = false;
 
-  gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
+  RefPtr<gfx::VRHMDInfo> hmdInfo = gfx::VRManager::Get()->GetDevice(aContainer->GetVRDeviceID());
   if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
     // we're not going to do anything here; instead, we'll do it all in ContainerRender.
     // XXX fix this; we can win with the same optimizations.  Specifically, we
     // want to render thebes layers only once and then composite the intermeidate surfaces
     // with different transforms twice.
     return;
   }
 
@@ -688,17 +689,17 @@ RenderIntermediate(ContainerT* aContaine
 
 template<class ContainerT> void
 ContainerRender(ContainerT* aContainer,
                  LayerManagerComposite* aManager,
                  const gfx::IntRect& aClipRect)
 {
   MOZ_ASSERT(aContainer->mPrepared);
 
-  gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
+  RefPtr<gfx::VRHMDInfo> hmdInfo = gfx::VRManager::Get()->GetDevice(aContainer->GetVRDeviceID());
   if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
     ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo);
     aContainer->mPrepared = nullptr;
     return;
   }
 
   if (aContainer->UseIntermediateSurface()) {
     RefPtr<CompositingRenderTarget> surface;
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -671,17 +671,17 @@ CompositorD3D11::DrawVRDistortion(const 
   HRESULT hr;
 
   EffectVRDistortion* vrEffect =
     static_cast<EffectVRDistortion*>(aEffectChain.mPrimaryEffect.get());
 
   TextureSourceD3D11* source = vrEffect->mTexture->AsSourceD3D11();
 
   VRHMDInfo* hmdInfo = vrEffect->mHMD;
-  VRHMDType hmdType = hmdInfo->GetType();
+  VRHMDType hmdType = hmdInfo->GetDeviceInfo().GetType();
 
   if (!mAttachments->mVRDistortionVS[hmdType] ||
       !mAttachments->mVRDistortionPS[hmdType])
   {
     NS_WARNING("No VS/PS for hmd type for VR distortion!");
     return;
   }
 
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -23,16 +23,17 @@
 #endif
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "mozilla/AutoRestore.h"        // for AutoRestore
 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/gfx/2D.h"          // for DrawTarget
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"          // for IntSize
+#include "VRManager.h"                  // for VRManager
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "mozilla/layers/APZThreadUtils.h"  // for APZCTreeManager
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorLRU.h"  // for CompositorLRU
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
@@ -74,16 +75,22 @@
 
 #ifdef MOZ_WIDGET_GONK
 #include "GeckoTouchDispatcher.h"
 #endif
 
 #include "LayerScope.h"
 
 namespace mozilla {
+
+namespace gfx {
+// See VRManagerChild.cpp
+void ReleaseVRManagerParentSingleton();
+} // namespace gfx
+
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace std;
 
 using base::ProcessId;
 using base::Thread;
@@ -421,16 +428,17 @@ CompositorVsyncScheduler::Composite(Time
 {
   MOZ_ASSERT(CompositorParent::IsInCompositorThread());
   {
     MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
     mCurrentCompositeTask = nullptr;
   }
 
   DispatchTouchEvents(aVsyncTimestamp);
+  DispatchVREvents(aVsyncTimestamp);
 
   if (mNeedsComposite || mAsapScheduling) {
     mNeedsComposite = 0;
     mLastCompose = aVsyncTimestamp;
     ComposeToTarget(nullptr);
     mVsyncNotificationsSkipped = 0;
 
     TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
@@ -492,30 +500,40 @@ CompositorVsyncScheduler::UnobserveVsync
 void
 CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
 {
 #ifdef MOZ_WIDGET_GONK
   GeckoTouchDispatcher::GetInstance()->NotifyVsync(aVsyncTimestamp);
 #endif
 }
 
+void
+CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
+{
+  MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+
+  VRManager* vm = VRManager::Get();
+  vm->NotifyVsync(aVsyncTimestamp);
+}
+
 void CompositorParent::StartUp()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
   MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
 
   sCompositorThreadHolder = new CompositorThreadHolder();
 }
 
 void CompositorParent::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
   MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!");
 
   ReleaseImageBridgeParentSingleton();
+  ReleaseVRManagerParentSingleton();
   MediaSystemResourceService::Shutdown();
 
   sCompositorThreadHolder = nullptr;
 
   // No locking is needed around sFinishedCompositorShutDown because it is only
   // ever accessed on the main thread.
   while (!sFinishedCompositorShutDown) {
     NS_ProcessNextEvent(nullptr, true);
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -129,16 +129,17 @@ public:
  
 private:
   virtual ~CompositorVsyncScheduler();
 
   void NotifyCompositeTaskExecuted();
   void ObserveVsync();
   void UnobserveVsync();
   void DispatchTouchEvents(TimeStamp aVsyncTimestamp);
+  void DispatchVREvents(TimeStamp aVsyncTimestamp);
   void CancelCurrentSetNeedsCompositeTask();
 
   class Observer final : public VsyncObserver
   {
   public:
     explicit Observer(CompositorVsyncScheduler* aOwner);
     virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) override;
     void Destroy();
@@ -290,17 +291,18 @@ public:
   /**
    * Returns a pointer to the compositor corresponding to the given ID.
    */
   static CompositorParent* GetCompositor(uint64_t id);
 
   /**
    * Returns the compositor thread's message loop.
    *
-   * This message loop is used by CompositorParent and ImageBridgeParent.
+   * This message loop is used by CompositorParent, ImageBridgeParent,
+   * and VRManagerParent
    */
   static MessageLoop* CompositorLoop();
 
   /**
    * Creates the compositor thread and the global compositor map.
    */
   static void StartUp();
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -44,20 +44,16 @@
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/AsyncCompositionManager.h"
 
 typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
 
 using mozilla::layout::RenderFrameParent;
 
 namespace mozilla {
-namespace gfx {
-class VRHMDInfo;
-} // namespace gfx
-
 namespace layers {
 
 class PGrallocBufferParent;
 
 //--------------------------------------------------
 // Convenience accessors
 static ShadowLayerParent*
 cast(const PLayerParent* in)
@@ -397,22 +393,18 @@ LayerTransactionParent::RecvUpdate(Infal
         const ContainerLayerAttributes& attrs =
           specific.get_ContainerLayerAttributes();
         containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
         containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
         containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
                                              attrs.presShellResolution());
         containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride());
 
-        if (attrs.hmdInfo()) {
-          if (!IsSameProcess()) {
-            NS_WARNING("VR layers currently not supported with cross-process compositing");
-            return false;
-          }
-          containerLayer->SetVRHMDInfo(reinterpret_cast<mozilla::gfx::VRHMDInfo*>(attrs.hmdInfo()));
+        if (attrs.hmdDeviceID()) {
+          containerLayer->SetVRDeviceID(attrs.hmdDeviceID());
         }
 
         break;
       }
       case Specific::TColorLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   color layer"));
 
         ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite();
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -243,20 +243,17 @@ struct PaintedLayerAttributes {
 struct ContainerLayerAttributes {
   float preXScale;
   float preYScale;
   float inheritedXScale;
   float inheritedYScale;
   float presShellResolution;
   bool scaleToResolution;
   EventRegionsOverride eventRegionsOverride;
-  // This is a bare pointer; LayerTransactionParent::RecvUpdate prevents this
-  // from being used when !IsSameProcess(), but we should make this truly
-  // cross process at some point by passing the HMDConfig
-  uint64_t hmdInfo;
+  uint32_t hmdDeviceID;
 };
 struct ColorLayerAttributes     { LayerColor color; IntRect bounds; };
 struct CanvasLayerAttributes    { Filter filter; IntRect bounds; };
 struct RefLayerAttributes {
   int64_t id;
   // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override
   // explicitly here.
   EventRegionsOverride eventRegionsOverride;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -14,17 +14,16 @@
 #include "mozilla/Logging.h"
 #include "mozilla/Services.h"
 #include "prprf.h"
 
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "gfxTextRun.h"
-#include "gfxVR.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
 
@@ -122,16 +121,18 @@ class mozilla::gl::SkiaGLGlue : public G
 
 #include "nsAlgorithm.h"
 #include "nsIGfxInfo.h"
 #include "nsIXULRuntime.h"
 #include "VsyncSource.h"
 #include "SoftwareVsyncSource.h"
 #include "nscore.h" // for NS_FREE_PERMANENT_DATA
 #include "mozilla/dom/ContentChild.h"
+#include "gfxVR.h"
+#include "VRManagerChild.h"
 
 namespace mozilla {
 namespace layers {
 #ifdef MOZ_WIDGET_GONK
 void InitGralloc();
 #endif
 void ShutdownTileCache();
 } // namespace layers
@@ -440,18 +441,17 @@ gfxPlatform::gfxPlatform()
     mSkiaGlue = nullptr;
 
     uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
     uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
     InitBackendPrefs(canvasMask, BackendType::CAIRO,
                      contentMask, BackendType::CAIRO);
     mTotalSystemMemory = mozilla::hal::GetTotalSystemMemory();
 
-    // give HMDs a chance to be initialized very early on
-    VRHMDManager::ManagerInit();
+    VRManager::ManagerInit();
 }
 
 gfxPlatform*
 gfxPlatform::GetPlatform()
 {
     if (!gPlatform) {
         Init();
     }
@@ -730,48 +730,49 @@ gfxPlatform::InitLayersIPC()
 
     if (XRE_IsParentProcess())
     {
         mozilla::layers::CompositorParent::StartUp();
 #ifdef MOZ_WIDGET_GONK
         SharedBufferManagerChild::StartUp();
 #endif
         mozilla::layers::ImageBridgeChild::StartUp();
+        gfx::VRManagerChild::StartUpSameProcess();
     }
 }
 
 /* static */ void
 gfxPlatform::ShutdownLayersIPC()
 {
     if (!sLayersIPCIsUp) {
       return;
     }
     sLayersIPCIsUp = false;
 
     if (XRE_IsParentProcess())
     {
         // This must happen after the shutdown of media and widgets, which
         // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
+        gfx::VRManagerChild::ShutDown();
         layers::ImageBridgeChild::ShutDown();
 #ifdef MOZ_WIDGET_GONK
         layers::SharedBufferManagerChild::ShutDown();
 #endif
 
         layers::CompositorParent::ShutDown();
-    }
+	} else if (XRE_GetProcessType() == GeckoProcessType_Content) {
+		gfx::VRManagerChild::ShutDown();
+	}
 }
 
 gfxPlatform::~gfxPlatform()
 {
     mScreenReferenceSurface = nullptr;
     mScreenReferenceDrawTarget = nullptr;
 
-    // Clean up any VR stuff
-    VRHMDManager::ManagerDestroy();
-
     // The cairo folks think we should only clean up in debug builds,
     // but we're generally in the habit of trying to shut down as
     // cleanly as possible even in production code, so call this
     // cairo_debug_* function unconditionally.
     //
     // because cairo can assert and thus crash on shutdown, don't do this in release builds
 #ifdef NS_FREE_PERMANENT_DATA
 #ifdef USE_SKIA
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRDeviceProxy.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 "prmem.h"
+#include "prenv.h"
+#include "gfxPrefs.h"
+#include "nsString.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/unused.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIScreenManager.h"
+
+
+#ifdef XP_WIN
+#include "../layers/d3d11/CompositorD3D11.h"
+#endif
+
+#include "VRDeviceProxy.h"
+#include "VRManagerChild.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+VRDeviceProxy::VRDeviceProxy(const VRDeviceUpdate& aDeviceUpdate)
+  : mDeviceInfo(aDeviceUpdate.mDeviceInfo)
+  , mSensorState(aDeviceUpdate.mSensorState)
+{
+  MOZ_COUNT_CTOR(VRDeviceProxy);
+
+  if (mDeviceInfo.mScreenRect.width && mDeviceInfo.mScreenRect.height) {
+    if (mDeviceInfo.mIsFakeScreen) {
+      mScreen = MakeFakeScreen(mDeviceInfo.mScreenRect);
+    } else {
+      nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
+      if (screenmgr) {
+        screenmgr->ScreenForRect(mDeviceInfo.mScreenRect.x, mDeviceInfo.mScreenRect.y,
+                                 mDeviceInfo.mScreenRect.width, mDeviceInfo.mScreenRect.height,
+                                 getter_AddRefs(mScreen));
+      }
+    }
+#ifdef DEBUG
+    printf_stderr("VR DEVICE SCREEN: %d %d %d %d\n",
+                  mDeviceInfo.mScreenRect.x, mDeviceInfo.mScreenRect.y,
+                  mDeviceInfo.mScreenRect.width, mDeviceInfo.mScreenRect.height);
+#endif
+  }
+}
+
+VRDeviceProxy::~VRDeviceProxy() {
+  MOZ_COUNT_DTOR(VRDeviceProxy);
+}
+
+void
+VRDeviceProxy::UpdateDeviceInfo(const VRDeviceUpdate& aDeviceUpdate)
+{
+  mDeviceInfo = aDeviceUpdate.mDeviceInfo;
+  mSensorState = aDeviceUpdate.mSensorState;
+}
+
+bool
+VRDeviceProxy::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
+                       double zNear, double zFar)
+{
+  VRManagerChild *vm = VRManagerChild::Get();
+  vm->SendSetFOV(mDeviceInfo.mDeviceID, aFOVLeft, aFOVRight, zNear, zFar);
+  return true;
+}
+
+void
+VRDeviceProxy::ZeroSensor()
+{
+  VRManagerChild *vm = VRManagerChild::Get();
+  vm->SendResetSensor(mDeviceInfo.mDeviceID);
+}
+
+VRHMDSensorState
+VRDeviceProxy::GetSensorState(double timeOffset)
+{
+  VRManagerChild *vm = VRManagerChild::Get();
+  Unused << vm->SendKeepSensorTracking(mDeviceInfo.mDeviceID);
+  return mSensorState;
+}
+
+void
+VRDeviceProxy::UpdateSensorState(const VRHMDSensorState& aSensorState)
+{
+  mSensorState = aSensorState;
+}
+
+// Dummy nsIScreen implementation, for when we just need to specify a size
+class FakeScreen : public nsIScreen
+{
+public:
+  explicit FakeScreen(const IntRect& aScreenRect)
+    : mScreenRect(aScreenRect)
+  { }
+
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD GetRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
+    *l = mScreenRect.x;
+    *t = mScreenRect.y;
+    *w = mScreenRect.width;
+    *h = mScreenRect.height;
+    return NS_OK;
+  }
+  NS_IMETHOD GetAvailRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
+    return GetRect(l, t, w, h);
+  }
+  NS_IMETHOD GetRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
+    return GetRect(l, t, w, h);
+  }
+  NS_IMETHOD GetAvailRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
+    return GetAvailRect(l, t, w, h);
+  }
+
+  NS_IMETHOD GetId(uint32_t* aId) override { *aId = (uint32_t)-1; return NS_OK; }
+  NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override { *aPixelDepth = 24; return NS_OK; }
+  NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override { *aColorDepth = 24; return NS_OK; }
+
+  NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; }
+  NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; }
+  NS_IMETHOD GetRotation(uint32_t* aRotation) override {
+    *aRotation = nsIScreen::ROTATION_0_DEG;
+    return NS_OK;
+  }
+  NS_IMETHOD SetRotation(uint32_t aRotation) override { return NS_ERROR_NOT_AVAILABLE; }
+  NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override {
+    *aContentsScaleFactor = 1.0;
+    return NS_OK;
+  }
+
+protected:
+  virtual ~FakeScreen() {}
+
+  IntRect mScreenRect;
+};
+
+NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen)
+
+
+/* static */ already_AddRefed<nsIScreen>
+VRDeviceProxy::MakeFakeScreen(const IntRect& aScreenRect)
+{
+  nsCOMPtr<nsIScreen> screen = new FakeScreen(aScreenRect);
+  return screen.forget();
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRDeviceProxy.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_PROXY_H
+#define GFX_VR_PROXY_H
+
+#include "nsIScreen.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+#include "gfxVR.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRManagerChild;
+
+class VRDeviceProxy
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRDeviceProxy)
+
+  explicit VRDeviceProxy(const VRDeviceUpdate& aDeviceUpdate);
+
+  void UpdateDeviceInfo(const VRDeviceUpdate& aDeviceUpdate);
+  void UpdateSensorState(const VRHMDSensorState& aSensorState);
+
+  const VRDeviceInfo& GetDeviceInfo() const { return mDeviceInfo; }
+  virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0);
+
+  bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
+              double zNear, double zFar);
+
+  virtual void ZeroSensor();
+
+
+  // The nsIScreen that represents this device
+  nsIScreen* GetScreen() { return mScreen; }
+
+protected:
+  virtual ~VRDeviceProxy();
+
+  VRDeviceInfo mDeviceInfo;
+  VRHMDSensorState mSensorState;
+
+  nsCOMPtr<nsIScreen> mScreen;
+
+  static already_AddRefed<nsIScreen> MakeFakeScreen(const IntRect& aScreenRect);
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_PROXY_H */
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRDeviceProxyOrientationFallBack.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 "VRDeviceProxyOrientationFallBack.h"
+#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientationInternal
+#include "mozilla/Hal.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+// 1/sqrt(2) (aka sqrt(2)/2)
+#ifndef M_SQRT1_2
+# define M_SQRT1_2	0.70710678118654752440
+#endif
+
+#ifdef ANDROID
+#include <android/log.h>
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoVR" , ## args)
+#else
+#define LOG(...) do { } while(0)
+#endif
+
+namespace {
+// some utility functions
+
+// This remaps axes in the given matrix to a new configuration based on the
+// screen orientation.  Similar to what Android SensorManager.remapCoordinateSystem
+// does, except only for a fixed number of transforms that we need.
+Matrix4x4
+RemapMatrixForOrientation(dom::ScreenOrientationInternal screenConfig, const Matrix4x4& aMatrix)
+{
+  Matrix4x4 out;
+  const float *in = &aMatrix._11;
+  float *o = &out._11;
+
+  if (screenConfig == dom::eScreenOrientation_LandscapePrimary) {
+    // remap X,Y -> Y,-X
+    o[0] = -in[1]; o[1] = in[0]; o[2] = in[2];
+    o[4] = -in[5]; o[5] = in[4]; o[6] = in[6];
+    o[8] = -in[9]; o[9] = in[8]; o[10] = in[10];
+  } else if (screenConfig == dom::eScreenOrientation_LandscapeSecondary) {
+    // remap X,Y -> -Y,X
+    o[0] = in[1]; o[1] = -in[0]; o[2] = in[2];
+    o[4] = in[5]; o[5] = -in[4]; o[6] = in[6];
+    o[8] = in[9]; o[9] = -in[8]; o[10] = in[10];
+  } else if (screenConfig == dom::eScreenOrientation_PortraitPrimary) {
+    out = aMatrix;
+  } else if (screenConfig == dom::eScreenOrientation_PortraitSecondary) {
+    // remap X,Y -> X,-Z
+    o[0] = in[0]; o[1] = in[2]; o[2] = -in[1];
+    o[4] = in[4]; o[5] = in[6]; o[6] = -in[5];
+    o[8] = in[8]; o[9] = in[10]; o[10] = -in[9];
+  } else {
+    MOZ_ASSERT(0, "gfxVRCardboard::RemapMatrixForOrientation invalid screenConfig");
+  }
+
+  return out;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace gfx {
+
+VRDeviceProxyOrientationFallBack::VRDeviceProxyOrientationFallBack(const VRDeviceUpdate& aDeviceUpdate)
+  : VRDeviceProxy(aDeviceUpdate)
+  , mOrient(dom::eScreenOrientation_PortraitPrimary)
+  , mTracking(false)
+{
+  MOZ_COUNT_CTOR_INHERITED(VRDeviceProxyOrientationFallBack, VRDeviceProxy);
+}
+
+VRDeviceProxyOrientationFallBack::~VRDeviceProxyOrientationFallBack()
+{
+  StopSensorTracking();
+  MOZ_COUNT_DTOR_INHERITED(VRDeviceProxyOrientationFallBack, VRDeviceProxy);
+}
+
+void
+VRDeviceProxyOrientationFallBack::StartSensorTracking()
+{
+  if (!mTracking) {
+    // it's never been started before; initialize observers and
+    // initial state.
+
+    hal::ScreenConfiguration sconfig;
+    hal::GetCurrentScreenConfiguration(&sconfig);
+    this->Notify(sconfig);
+
+    hal::RegisterSensorObserver(hal::SENSOR_GAME_ROTATION_VECTOR, this);
+    hal::RegisterScreenConfigurationObserver(this);
+
+    mSensorState.Clear();
+
+    mTracking = true;
+  }
+}
+
+void
+VRDeviceProxyOrientationFallBack::StopSensorTracking()
+{
+  if (mTracking) {
+    hal::UnregisterScreenConfigurationObserver(this);
+    hal::UnregisterSensorObserver(hal::SENSOR_GAME_ROTATION_VECTOR, this);
+    mTracking = false;
+  }
+}
+
+// Android sends us events that have a 90-degree rotation about 
+// the x axis compared to what we want (phone flat vs. phone held in front of the eyes).
+// Correct for this by applying a transform to undo this rotation.
+void
+VRDeviceProxyOrientationFallBack::Notify(const hal::ScreenConfiguration& config)
+{
+  mOrient = config.orientation();
+
+  if (mOrient == dom::eScreenOrientation_LandscapePrimary) {
+    mScreenTransform = Quaternion(-0.5f, 0.5f, 0.5f, 0.5f);
+  } else if (mOrient == dom::eScreenOrientation_LandscapeSecondary) {
+    mScreenTransform = Quaternion(-0.5f, -0.5f, -0.5f, 0.5f);
+  } else if (mOrient == dom::eScreenOrientation_PortraitPrimary) {
+    mScreenTransform = Quaternion((float) -M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2);
+  } else if (mOrient == dom::eScreenOrientation_PortraitSecondary) {
+    // Currently, PortraitSecondary event doesn't be triggered.
+    mScreenTransform = Quaternion((float) M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2);
+  }
+}
+
+void
+VRDeviceProxyOrientationFallBack::Notify(const hal::SensorData& data)
+{
+  if (data.sensor() != hal::SENSOR_GAME_ROTATION_VECTOR)
+    return;
+
+  const nsTArray<float>& sensorValues = data.values();
+
+  // This is super chatty
+  //LOG("HMDInfoCardboard::Notify %f %f %f %f\n", sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]);
+
+  mSavedLastSensor.Set(sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]);
+  mSavedLastSensorTime = data.timestamp();
+  mNeedsSensorCompute = true;
+}
+
+void
+VRDeviceProxyOrientationFallBack::ZeroSensor()
+{
+  mSensorZeroInverse = mSavedLastSensor;
+  mSensorZeroInverse.Invert();
+}
+
+void
+VRDeviceProxyOrientationFallBack::ComputeStateFromLastSensor()
+{
+  if (!mNeedsSensorCompute)
+    return;
+
+  // apply the zero orientation
+  Quaternion q = mSensorZeroInverse * mSavedLastSensor;
+
+  // make a matrix from the quat
+  Matrix4x4 qm;
+  qm.SetRotationFromQuaternion(q);
+
+  // remap the coordinate space, based on the orientation
+  Matrix4x4 qmRemapped = RemapMatrixForOrientation(mOrient, qm);
+
+  // turn it back into a quat
+  q.SetFromRotationMatrix(qmRemapped);
+
+  // apply adjustment based on what's been done to the screen and the original zero
+  // position of the base coordinate space
+  q = mScreenTransform * q;
+
+  mSensorState.flags |= VRStateValidFlags::State_Orientation;
+  mSensorState.orientation[0] = q.x;
+  mSensorState.orientation[1] = q.y;
+  mSensorState.orientation[2] = q.z;
+  mSensorState.orientation[3] = q.w;
+
+  mSensorState.timestamp = mSavedLastSensorTime / 1000000.0;
+
+  mNeedsSensorCompute = false;
+}
+
+VRHMDSensorState
+VRDeviceProxyOrientationFallBack::GetSensorState(double timeOffset)
+{
+  StartSensorTracking();
+  ComputeStateFromLastSensor();
+  return mSensorState;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRDeviceProxyOrientationFallBack.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_PROXY_ORIENTATION_FALLBACK_H
+#define GFX_VR_PROXY_ORIENTATION_FALLBACK_H
+
+#include "VRDeviceProxy.h"
+
+#include "mozilla/HalSensor.h"
+#include "mozilla/HalScreenConfiguration.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRDeviceProxyOrientationFallBack : public VRDeviceProxy
+                                       , public hal::ISensorObserver
+                                       , public hal::ScreenConfigurationObserver
+{
+public:
+
+  explicit VRDeviceProxyOrientationFallBack(const VRDeviceUpdate& aDeviceUpdate);
+
+  virtual void ZeroSensor() override;
+  virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) override;
+
+  // ISensorObserver interface
+  void Notify(const hal::SensorData& SensorData) override;
+  // ScreenConfigurationObserver interface
+  void Notify(const hal::ScreenConfiguration& ScreenConfiguration) override;
+
+
+protected:
+  virtual ~VRDeviceProxyOrientationFallBack();
+
+  void StartSensorTracking();
+  void StopSensorTracking();
+  void ComputeStateFromLastSensor();
+
+  uint32_t mOrient;
+  Quaternion mScreenTransform;
+  Quaternion mSensorZeroInverse;
+  Quaternion mSavedLastSensor;
+  double mSavedLastSensorTime;
+  bool mNeedsSensorCompute;    // if we need to compute the state from mSavedLastSensor
+
+  bool mTracking;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_VR_PROXY_ORIENTATION_FALLBACK_H */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRManager.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 "VRManager.h"
+#include "VRManagerParent.h"
+#include "gfxVR.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/VRDevice.h"
+#include "mozilla/unused.h"
+
+#include "gfxPrefs.h"
+#include "gfxVR.h"
+#if defined(XP_WIN)
+#include "gfxVROculus.h"
+#endif
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
+#include "gfxVROculus050.h"
+#endif
+#include "gfxVRCardboard.h"
+
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRManager> sVRManagerSingleton;
+
+/*static*/ void
+VRManager::ManagerInit()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sVRManagerSingleton == nullptr) {
+    sVRManagerSingleton = new VRManager();
+    ClearOnShutdown(&sVRManagerSingleton);
+  }
+}
+
+VRManager::VRManager()
+  : mInitialized(false)
+{
+  MOZ_COUNT_CTOR(VRManager);
+  MOZ_ASSERT(sVRManagerSingleton == nullptr);
+
+  RefPtr<VRHMDManager> mgr;
+
+  // we'll only load the 0.5.0 oculus runtime if
+  // the >= 0.6.0 one failed to load; otherwise
+  // we might initialize oculus twice
+  bool useOculus050 = true;
+  Unused << useOculus050;
+
+#if defined(XP_WIN)
+  mgr = VRHMDManagerOculus::Create();
+  if (mgr) {
+    useOculus050 = false;
+    mManagers.AppendElement(mgr);
+  }
+#endif
+
+#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
+  if (useOculus050) {
+    mgr = VRHMDManagerOculus050::Create();
+    if (mgr) {
+      mManagers.AppendElement(mgr);
+    }
+  }
+#endif
+
+  mgr = VRHMDManagerCardboard::Create();
+  if (mgr) {
+    mManagers.AppendElement(mgr);
+  }
+}
+
+VRManager::~VRManager()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mInitialized);
+  MOZ_COUNT_DTOR(VRManager);
+}
+
+void
+VRManager::Destroy()
+{
+  for (uint32_t i = 0; i < mManagers.Length(); ++i) {
+    mManagers[i]->Destroy();
+  }
+  mInitialized = false;
+}
+
+void
+VRManager::Init()
+{
+  for (uint32_t i = 0; i < mManagers.Length(); ++i) {
+    mManagers[i]->Init();
+  }
+  mInitialized = true;
+}
+
+/* static */VRManager*
+VRManager::Get()
+{
+  MOZ_ASSERT(sVRManagerSingleton != nullptr);
+
+  return sVRManagerSingleton;
+}
+
+void
+VRManager::AddVRManagerParent(VRManagerParent* aVRManagerParent)
+{
+  if (mVRManagerParents.IsEmpty()) {
+    Init();
+  }
+  mVRManagerParents.PutEntry(aVRManagerParent);
+}
+
+void
+VRManager::RemoveVRManagerParent(VRManagerParent* aVRManagerParent)
+{
+  mVRManagerParents.RemoveEntry(aVRManagerParent);
+  if (mVRManagerParents.IsEmpty()) {
+    Destroy();
+  }
+}
+
+void
+VRManager::NotifyVsync(const TimeStamp& aVsyncTimestamp)
+{
+  for (auto iter = mVRDevices.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRHMDInfo* device = iter.UserData();
+    device->NotifyVsync(aVsyncTimestamp);
+  }
+  DispatchVRDeviceSensorUpdate();
+}
+
+void
+VRManager::RefreshVRDevices()
+{
+  nsTArray<RefPtr<gfx::VRHMDInfo> > devices;
+
+  for (uint32_t i = 0; i < mManagers.Length(); ++i) {
+    mManagers[i]->GetHMDs(devices);
+  }
+
+  bool deviceInfoChanged = false;
+
+  if (devices.Length() != mVRDevices.Count()) {
+    deviceInfoChanged = true;
+  }
+
+  for (const auto& device: devices) {
+    RefPtr<VRHMDInfo> oldDevice = GetDevice(device->GetDeviceInfo().GetDeviceID());
+    if (oldDevice == nullptr) {
+      deviceInfoChanged = true;
+      break;
+    }
+    if (oldDevice->GetDeviceInfo() != device->GetDeviceInfo()) {
+      deviceInfoChanged = true;
+      break;
+    }
+  }
+
+  if (deviceInfoChanged) {
+    mVRDevices.Clear();
+    for (const auto& device: devices) {
+      mVRDevices.Put(device->GetDeviceInfo().GetDeviceID(), device);
+    }
+  }
+
+  DispatchVRDeviceInfoUpdate();
+}
+
+void
+VRManager::DispatchVRDeviceInfoUpdate()
+{
+  nsTArray<VRDeviceUpdate> update;
+  for (auto iter = mVRDevices.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRHMDInfo* device = iter.UserData();
+    update.AppendElement(VRDeviceUpdate(device->GetDeviceInfo(),
+                                        device->GetSensorState()));
+  }
+
+  for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+    Unused << iter.Get()->GetKey()->SendUpdateDeviceInfo(update);
+  }
+}
+
+void
+VRManager::DispatchVRDeviceSensorUpdate()
+{
+  nsTArray<VRSensorUpdate> update;
+
+  for (auto iter = mVRDevices.Iter(); !iter.Done(); iter.Next()) {
+    gfx::VRHMDInfo* device = iter.UserData();
+    if (!device->GetDeviceInfo().GetUseMainThreadOrientation()) {
+      update.AppendElement(VRSensorUpdate(device->GetDeviceInfo().GetDeviceID(),
+                                          device->GetSensorState()));
+    }
+  }
+  if (update.Length() > 0) {
+    for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
+      Unused << iter.Get()->GetKey()->SendUpdateDeviceSensors(update);
+    }
+  }
+}
+
+RefPtr<gfx::VRHMDInfo>
+VRManager::GetDevice(const uint32_t& aDeviceID)
+{
+  RefPtr<gfx::VRHMDInfo> device;
+  if (mVRDevices.Get(aDeviceID, getter_AddRefs(device))) {
+    return device;
+  }
+  return nullptr;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRManager.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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_MANAGER_H
+#define GFX_VR_MANAGER_H
+
+#include "nsAutoPtr.h"
+#include "nsRefPtrHashtable.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "mozilla/TimeStamp.h"
+#include "gfxVR.h"
+
+namespace mozilla {
+namespace gfx {
+
+class VRManagerParent;
+class VRHMDInfo;
+
+class VRManager
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::gfx::VRManager)
+
+public:
+  static void ManagerInit();
+  static VRManager* Get();
+
+  void AddVRManagerParent(VRManagerParent* aVRManagerParent);
+  void RemoveVRManagerParent(VRManagerParent* aVRManagerParent);
+
+  void NotifyVsync(const TimeStamp& aVsyncTimestamp);
+  void RefreshVRDevices();
+  RefPtr<gfx::VRHMDInfo> GetDevice(const uint32_t& aDeviceID);
+
+protected:
+  VRManager();
+  ~VRManager();
+
+private:
+
+  void Init();
+  void Destroy();
+
+  void DispatchVRDeviceInfoUpdate();
+  void DispatchVRDeviceSensorUpdate();
+
+  typedef nsTHashtable<nsRefPtrHashKey<VRManagerParent>> VRManagerParentSet;
+  VRManagerParentSet mVRManagerParents;
+
+  typedef nsTArray<RefPtr<VRHMDManager>> VRHMDManagerArray;
+  VRHMDManagerArray mManagers;
+
+  typedef nsRefPtrHashtable<nsUint32HashKey, gfx::VRHMDInfo> VRHMDInfoHashMap;
+  VRHMDInfoHashMap mVRDevices;
+
+  Atomic<bool> mInitialized;
+
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_MANAGER_H
--- a/gfx/vr/gfxVR.cpp
+++ b/gfx/vr/gfxVR.cpp
@@ -15,169 +15,48 @@
 #if defined(XP_WIN)
 #include "gfxVROculus.h"
 #endif
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
 #include "gfxVROculus050.h"
 #endif
 #include "gfxVRCardboard.h"
 
-#include "nsServiceManagerUtils.h"
-#include "nsIScreenManager.h"
-
 #include "mozilla/unused.h"
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/TextureHost.h"
 
 #ifndef M_PI
 # define M_PI 3.14159265358979323846
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
-// Dummy nsIScreen implementation, for when we just need to specify a size
-class FakeScreen : public nsIScreen
-{
-public:
-  explicit FakeScreen(const IntRect& aScreenRect)
-    : mScreenRect(aScreenRect)
-  { }
-
-  NS_DECL_ISUPPORTS
-
-  NS_IMETHOD GetRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
-    *l = mScreenRect.x;
-    *t = mScreenRect.y;
-    *w = mScreenRect.width;
-    *h = mScreenRect.height;
-    return NS_OK;
-  }
-  NS_IMETHOD GetAvailRect(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
-    return GetRect(l, t, w, h);
-  }
-  NS_IMETHOD GetRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
-    return GetRect(l, t, w, h);
-  }
-  NS_IMETHOD GetAvailRectDisplayPix(int32_t *l, int32_t *t, int32_t *w, int32_t *h) override {
-    return GetAvailRect(l, t, w, h);
-  }
+Atomic<uint32_t> VRHMDManager::sDeviceBase(0);
 
-  NS_IMETHOD GetId(uint32_t* aId) override { *aId = (uint32_t)-1; return NS_OK; }
-  NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override { *aPixelDepth = 24; return NS_OK; }
-  NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override { *aColorDepth = 24; return NS_OK; }
-
-  NS_IMETHOD LockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; }
-  NS_IMETHOD UnlockMinimumBrightness(uint32_t aBrightness) override { return NS_ERROR_NOT_AVAILABLE; }
-  NS_IMETHOD GetRotation(uint32_t* aRotation) override {
-    *aRotation = nsIScreen::ROTATION_0_DEG;
-    return NS_OK;
-  }
-  NS_IMETHOD SetRotation(uint32_t aRotation) override { return NS_ERROR_NOT_AVAILABLE; }
-  NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override {
-    *aContentsScaleFactor = 1.0;
-    return NS_OK;
-  }
-
-protected:
-  virtual ~FakeScreen() {}
-
-  IntRect mScreenRect;
-};
-
-NS_IMPL_ISUPPORTS(FakeScreen, nsIScreen)
-
-VRHMDInfo::VRHMDInfo(VRHMDType aType)
-  : mType(aType)
+VRHMDInfo::VRHMDInfo(VRHMDType aType, bool aUseMainThreadOrientation)
 {
   MOZ_COUNT_CTOR(VRHMDInfo);
-
-  mDeviceIndex = VRHMDManager::AllocateDeviceIndex();
-  mDeviceName.AssignLiteral("Unknown Device");
+  mDeviceInfo.mType = aType;
+  mDeviceInfo.mDeviceID = VRHMDManager::AllocateDeviceID();
+  mDeviceInfo.mUseMainThreadOrientation = aUseMainThreadOrientation;
 }
 
-
-VRHMDManager::VRHMDManagerArray *VRHMDManager::sManagers = nullptr;
-Atomic<uint32_t> VRHMDManager::sDeviceBase(0);
-
-/* static */ void
-VRHMDManager::ManagerInit()
+VRHMDInfo::~VRHMDInfo()
 {
-  if (sManagers)
-    return;
-
-  sManagers = new VRHMDManagerArray();
-
-  RefPtr<VRHMDManager> mgr;
-
-  // we'll only load the 0.5.0 oculus runtime if
-  // the >= 0.6.0 one failed to load; otherwise
-  // we might initialize oculus twice
-  bool useOculus050 = true;
-  Unused << useOculus050;
-
-#if defined(XP_WIN)
-  mgr = new VRHMDManagerOculus();
-  if (mgr->PlatformInit()) {
-    useOculus050 = false;
-    sManagers->AppendElement(mgr);
-  }
-#endif
-
-#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
-  if (useOculus050) {
-    mgr = new VRHMDManagerOculus050();
-    if (mgr->PlatformInit())
-      sManagers->AppendElement(mgr);
-  }
-#endif
-
-  mgr = new VRHMDManagerCardboard();
-  if (mgr->PlatformInit())
-    sManagers->AppendElement(mgr);
-}
-
-/* static */ void
-VRHMDManager::ManagerDestroy()
-{
-  if (!sManagers)
-    return;
-
-  for (uint32_t i = 0; i < sManagers->Length(); ++i) {
-    (*sManagers)[i]->Destroy();
-  }
-
-  delete sManagers;
-  sManagers = nullptr;
-}
-
-/* static */ void
-VRHMDManager::GetAllHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
-{
-  if (!sManagers)
-    return;
-
-  for (uint32_t i = 0; i < sManagers->Length(); ++i) {
-    (*sManagers)[i]->GetHMDs(aHMDResult);
-  }
+  MOZ_COUNT_DTOR(VRHMDInfo);
 }
 
 /* static */ uint32_t
-VRHMDManager::AllocateDeviceIndex()
+VRHMDManager::AllocateDeviceID()
 {
   return ++sDeviceBase;
 }
 
-/* static */ already_AddRefed<nsIScreen>
-VRHMDManager::MakeFakeScreen(int32_t x, int32_t y, uint32_t width, uint32_t height)
-{
-  nsCOMPtr<nsIScreen> screen = new FakeScreen(IntRect(x, y, width, height));
-  return screen.forget();
-}
-
 VRHMDRenderingSupport::RenderTargetSet::RenderTargetSet()
   : currentRenderTarget(0)
 {
 }
 
 VRHMDRenderingSupport::RenderTargetSet::~RenderTargetSet()
 {
 }
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -2,40 +2,50 @@
  * 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 "nsTArray.h"
-#include "nsIScreen.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/Atomics.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/TypedEnumBits.h"
 
 namespace mozilla {
 namespace layers {
 class Compositor;
 class CompositingRenderTarget;
 }
 
 namespace gfx {
 
 enum class VRHMDType : uint16_t {
   Oculus,
   Cardboard,
   Oculus050,
   NumHMDTypes
 };
 
+enum class VRStateValidFlags : uint16_t {
+  State_None = 0,
+  State_Position = 1 << 1,
+  State_Orientation = 1 << 2,
+  // State_All used for validity checking during IPC serialization
+  State_All = (1 << 3) - 1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRStateValidFlags)
+
 struct VRFieldOfView {
   VRFieldOfView() {}
   VRFieldOfView(double up, double right, double down, double left)
     : upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
   {}
 
   bool operator==(const VRFieldOfView& other) const {
     return other.upDegrees == upDegrees &&
@@ -58,47 +68,138 @@ struct VRFieldOfView {
   Matrix4x4 ConstructProjectionMatrix(float zNear, float zFar, bool rightHanded);
 
   double upDegrees;
   double rightDegrees;
   double downDegrees;
   double leftDegrees;
 };
 
-// 12 floats per vertex. Position, tex coordinates
-// for each channel, and 4 generic attributes
-struct VRDistortionConstants {
-  float eyeToSourceScaleAndOffset[4];
-  float destinationScaleAndOffset[4];
-};
-
 struct VRDistortionVertex {
   float values[12];
 };
 
 struct VRDistortionMesh {
   nsTArray<VRDistortionVertex> mVertices;
   nsTArray<uint16_t> mIndices;
 };
 
+// 12 floats per vertex. Position, tex coordinates
+// for each channel, and 4 generic attributes
+struct VRDistortionConstants {
+  float eyeToSourceScaleAndOffset[4];
+  float destinationScaleAndOffset[4];
+};
+
+struct VRDeviceInfo
+{
+  VRHMDType GetType() const { return mType; }
+  uint32_t GetDeviceID() const { return mDeviceID; }
+  const nsCString& GetDeviceName() const { return mDeviceName; }
+  VRStateValidFlags GetSupportedSensorStateBits() const { return mSupportedSensorBits; }
+  const VRFieldOfView& GetRecommendedEyeFOV(uint32_t whichEye) const { return mRecommendedEyeFOV[whichEye]; }
+  const VRFieldOfView& GetMaximumEyeFOV(uint32_t whichEye) const { return mMaximumEyeFOV[whichEye]; }
+
+  const IntSize& SuggestedEyeResolution() const { return mEyeResolution; }
+  const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; }
+  const Matrix4x4& GetEyeProjectionMatrix(uint32_t whichEye) const { return mEyeProjectionMatrix[whichEye]; }
+  const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const { return mEyeFOV[whichEye]; }
+  bool GetUseMainThreadOrientation() const { return mUseMainThreadOrientation; }
+
+  enum Eye {
+    Eye_Left,
+    Eye_Right,
+    NumEyes
+  };
+
+  uint32_t mDeviceID;
+  VRHMDType mType;
+  nsCString mDeviceName;
+  VRStateValidFlags mSupportedSensorBits;
+  VRFieldOfView mMaximumEyeFOV[VRDeviceInfo::NumEyes];
+  VRFieldOfView mRecommendedEyeFOV[VRDeviceInfo::NumEyes];
+  VRFieldOfView mEyeFOV[VRDeviceInfo::NumEyes];
+  Point3D mEyeTranslation[VRDeviceInfo::NumEyes];
+  Matrix4x4 mEyeProjectionMatrix[VRDeviceInfo::NumEyes];
+  /* Suggested resolution for rendering a single eye.
+   * Assumption is that left/right rendering will be 2x of this size.
+   * XXX fix this for vertical displays
+   */
+  IntSize mEyeResolution;
+  IntRect mScreenRect;
+
+  bool mIsFakeScreen;
+  bool mUseMainThreadOrientation;
+
+
+
+  bool operator==(const VRDeviceInfo& other) const {
+    return mType == other.mType &&
+           mDeviceID == other.mDeviceID &&
+           mDeviceName == other.mDeviceName &&
+           mSupportedSensorBits == other.mSupportedSensorBits &&
+           mEyeResolution == other.mEyeResolution &&
+           mScreenRect == other.mScreenRect &&
+           mIsFakeScreen == other.mIsFakeScreen &&
+           mUseMainThreadOrientation == other.mUseMainThreadOrientation &&
+           mMaximumEyeFOV[0] == other.mMaximumEyeFOV[0] &&
+           mMaximumEyeFOV[1] == other.mMaximumEyeFOV[1] &&
+           mRecommendedEyeFOV[0] == other.mRecommendedEyeFOV[0] &&
+           mRecommendedEyeFOV[1] == other.mRecommendedEyeFOV[1] &&
+           mEyeFOV[0] == other.mEyeFOV[0] &&
+           mEyeFOV[1] == other.mEyeFOV[1] &&
+           mEyeTranslation[0] == other.mEyeTranslation[0] &&
+           mEyeTranslation[1] == other.mEyeTranslation[1] &&
+           mEyeProjectionMatrix[0] == other.mEyeProjectionMatrix[0] &&
+           mEyeProjectionMatrix[1] == other.mEyeProjectionMatrix[1];
+  }
+
+  bool operator!=(const VRDeviceInfo& other) const {
+    return !(*this == other);
+  }
+};
+
+
+
 struct VRHMDSensorState {
   double timestamp;
-  uint32_t flags;
+  VRStateValidFlags flags;
   float orientation[4];
   float position[3];
   float angularVelocity[3];
   float angularAcceleration[3];
   float linearVelocity[3];
   float linearAcceleration[3];
 
   void Clear() {
     memset(this, 0, sizeof(VRHMDSensorState));
   }
 };
 
+struct VRSensorUpdate {
+  VRSensorUpdate() { }; // Required for ipdl binding
+  VRSensorUpdate(uint32_t aDeviceID, const VRHMDSensorState& aSensorState)
+   : mDeviceID(aDeviceID)
+   , mSensorState(aSensorState) { };
+
+  uint32_t mDeviceID;
+  VRHMDSensorState mSensorState;
+};
+
+struct VRDeviceUpdate {
+  VRDeviceUpdate() { }; // Required for ipdl binding
+  VRDeviceUpdate(const VRDeviceInfo& aDeviceInfo,
+                 const VRHMDSensorState& aSensorState)
+   : mDeviceInfo(aDeviceInfo)
+   , mSensorState(aSensorState) { };
+
+  VRDeviceInfo mDeviceInfo;
+  VRHMDSensorState mSensorState;
+};
+
 /* A pure data struct that can be used to see if
  * the configuration of one HMDInfo matches another; for rendering purposes,
  * really asking "can the rendering details of this one be used for the other"
  */
 struct VRHMDConfiguration {
   VRHMDConfiguration() : hmdType(VRHMDType::NumHMDTypes) {}
 
   bool operator==(const VRHMDConfiguration& other) const {
@@ -144,116 +245,65 @@ public:
   virtual already_AddRefed<RenderTargetSet> CreateRenderTargetSet(layers::Compositor *aCompositor, const IntSize& aSize) = 0;
   virtual void DestroyRenderTargetSet(RenderTargetSet *aRTSet) = 0;
   virtual void SubmitFrame(RenderTargetSet *aRTSet) = 0;
 protected:
   VRHMDRenderingSupport() { }
 };
 
 class VRHMDInfo {
-public:
-  enum Eye {
-    Eye_Left,
-    Eye_Right,
-    NumEyes
-  };
-
-  enum StateValidFlags {
-    State_Position = 1 << 1,
-    State_Orientation = 1 << 2
-  };
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDInfo)
 
-  VRHMDType GetType() const { return mType; }
-  uint32_t GetDeviceIndex() const { return mDeviceIndex; }
-  const nsCString& GetDeviceName() const { return mDeviceName; }
-
-  virtual const VRFieldOfView& GetRecommendedEyeFOV(uint32_t whichEye) { return mRecommendedEyeFOV[whichEye]; }
-  virtual const VRFieldOfView& GetMaximumEyeFOV(uint32_t whichEye) { return mMaximumEyeFOV[whichEye]; }
-
   const VRHMDConfiguration& GetConfiguration() const { return mConfiguration; }
+  const VRDeviceInfo& GetDeviceInfo() const { return mDeviceInfo; }
 
   /* set the FOV for this HMD unit; this triggers a computation of all the remaining bits.  Returns false if it fails */
   virtual bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
                       double zNear, double zFar) = 0;
-  const VRFieldOfView& GetEyeFOV(uint32_t whichEye)  { return mEyeFOV[whichEye]; }
 
-  /* Suggested resolution for rendering a single eye.
-   * Assumption is that left/right rendering will be 2x of this size.
-   * XXX fix this for vertical displays
-   */
-  const IntSize& SuggestedEyeResolution() const { return mEyeResolution; }
-  const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; }
-  const Matrix4x4& GetEyeProjectionMatrix(uint32_t whichEye) const { return mEyeProjectionMatrix[whichEye]; }
-
-  virtual uint32_t GetSupportedSensorStateBits() { return mSupportedSensorBits; }
-  virtual bool StartSensorTracking() = 0;
+  virtual bool KeepSensorTracking() = 0;
+  virtual void NotifyVsync(const TimeStamp& aVsyncTimestamp) = 0;
   virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) = 0;
-  virtual void StopSensorTracking() = 0;
 
   virtual void ZeroSensor() = 0;
 
-
   // if rendering is offloaded
   virtual VRHMDRenderingSupport *GetRenderingSupport() { return nullptr; }
 
   // distortion mesh stuff; we should implement renderingsupport for this
   virtual void FillDistortionConstants(uint32_t whichEye,
                                        const IntSize& textureSize, // the full size of the texture
                                        const IntRect& eyeViewport, // the viewport within the texture for the current eye
                                        const Size& destViewport,   // the size of the destination viewport
                                        const Rect& destRect,       // the rectangle within the dest viewport that this should be rendered
                                        VRDistortionConstants& values) = 0;
 
-  virtual const VRDistortionMesh& GetDistortionMesh(uint32_t whichEye) const { return mDistortionMesh[whichEye]; }
-
-  // The nsIScreen that represents this device
-  virtual nsIScreen* GetScreen() { return mScreen; }
+  const VRDistortionMesh& GetDistortionMesh(uint32_t whichEye) const { return mDistortionMesh[whichEye]; }
+protected:
+  explicit VRHMDInfo(VRHMDType aType, bool aUseMainThreadOrientation);
+  virtual ~VRHMDInfo();
 
-protected:
-  explicit VRHMDInfo(VRHMDType aType);
-  virtual ~VRHMDInfo() { MOZ_COUNT_DTOR(VRHMDInfo); }
-
-  VRHMDType mType;
   VRHMDConfiguration mConfiguration;
-  uint32_t mDeviceIndex;
-  nsCString mDeviceName;
-
-  VRFieldOfView mEyeFOV[NumEyes];
-  IntSize mEyeResolution;
-  Point3D mEyeTranslation[NumEyes];
-  Matrix4x4 mEyeProjectionMatrix[NumEyes];
-  VRDistortionMesh mDistortionMesh[NumEyes];
-  uint32_t mSupportedSensorBits;
-
-  VRFieldOfView mRecommendedEyeFOV[NumEyes];
-  VRFieldOfView mMaximumEyeFOV[NumEyes];
-
-  nsCOMPtr<nsIScreen> mScreen;
+  VRDeviceInfo mDeviceInfo;
+  VRDistortionMesh mDistortionMesh[VRDeviceInfo::NumEyes];
 };
 
 class VRHMDManager {
 public:
-  static void ManagerInit();
-  static void ManagerDestroy();
-  static void GetAllHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult);
-  static uint32_t AllocateDeviceIndex();
-  static already_AddRefed<nsIScreen> MakeFakeScreen(int32_t x, int32_t y, uint32_t width, uint32_t height);
+  static uint32_t AllocateDeviceID();
 
 protected:
-  typedef nsTArray<RefPtr<VRHMDManager>> VRHMDManagerArray;
-  static VRHMDManagerArray *sManagers;
   static Atomic<uint32_t> sDeviceBase;
 
+
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRHMDManager)
 
-  virtual bool PlatformInit() = 0;
   virtual bool Init() = 0;
   virtual void Destroy() = 0;
   virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult) = 0;
 
 protected:
   VRHMDManager() { }
   virtual ~VRHMDManager() { }
 };
--- a/gfx/vr/gfxVRCardboard.cpp
+++ b/gfx/vr/gfxVRCardboard.cpp
@@ -5,251 +5,105 @@
 
 #include <math.h>
 
 #include "prlink.h"
 #include "prmem.h"
 #include "prenv.h"
 #include "gfxPrefs.h"
 #include "nsString.h"
-#include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Hal.h"
 
 #include "gfxVRCardboard.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsIScreenManager.h"
 
-#ifdef ANDROID
-#include <android/log.h>
-#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoVR" , ## args)
-#else
-#define LOG(...) do { } while(0)
-#endif
-
-// 1/sqrt(2) (aka sqrt(2)/2)
-#ifndef M_SQRT1_2
-# define M_SQRT1_2	0.70710678118654752440
-#endif
-
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 
-namespace {
-// some utility functions
-
-// This remaps axes in the given matrix to a new configuration based on the
-// screen orientation.  Similar to what Android SensorManager.remapCoordinateSystem
-// does, except only for a fixed number of transforms that we need.
-Matrix4x4
-RemapMatrixForOrientation(ScreenOrientationInternal screenConfig, const Matrix4x4& aMatrix)
-{
-  Matrix4x4 out;
-  const float *in = &aMatrix._11;
-  float *o = &out._11;
-
-  if (screenConfig == eScreenOrientation_LandscapePrimary) {
-    // remap X,Y -> Y,-X
-    o[0] = -in[1]; o[1] = in[0]; o[2] = in[2];
-    o[4] = -in[5]; o[5] = in[4]; o[6] = in[6];
-    o[8] = -in[9]; o[9] = in[8]; o[10] = in[10];
-  } else if (screenConfig == eScreenOrientation_LandscapeSecondary) {
-    // remap X,Y -> -Y,X
-    o[0] = in[1]; o[1] = -in[0]; o[2] = in[2];
-    o[4] = in[5]; o[5] = -in[4]; o[6] = in[6];
-    o[8] = in[9]; o[9] = -in[8]; o[10] = in[10];
-  } else if (screenConfig == eScreenOrientation_PortraitPrimary) {
-    out = aMatrix;
-  } else if (screenConfig == eScreenOrientation_PortraitSecondary) {
-    // remap X,Y -> X,-Z
-    o[0] = in[0]; o[1] = in[2]; o[2] = -in[1];
-    o[4] = in[4]; o[5] = in[6]; o[6] = -in[5];
-    o[8] = in[8]; o[9] = in[10]; o[10] = -in[9];
-  } else {
-    MOZ_ASSERT(0, "gfxVRCardboard::RemapMatrixForOrientation invalid screenConfig");
-  }
-
-  return out;
-}
-
-} // namespace
-
 HMDInfoCardboard::HMDInfoCardboard()
-  : VRHMDInfo(VRHMDType::Cardboard)
-  , mStartCount(0)
-  , mOrient(eScreenOrientation_PortraitPrimary)
+  : VRHMDInfo(VRHMDType::Cardboard, true)
 {
   MOZ_ASSERT(sizeof(HMDInfoCardboard::DistortionVertex) == sizeof(VRDistortionVertex),
              "HMDInfoCardboard::DistortionVertex must match the size of VRDistortionVertex");
 
   MOZ_COUNT_CTOR_INHERITED(HMDInfoCardboard, VRHMDInfo);
 
-  mDeviceName.AssignLiteral("Phone Sensor (Cardboard) HMD");
-
-  mSupportedSensorBits = State_Orientation;
-
-  mRecommendedEyeFOV[Eye_Left] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
-  mRecommendedEyeFOV[Eye_Right] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
-
-  mMaximumEyeFOV[Eye_Left] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
-  mMaximumEyeFOV[Eye_Right] = VRFieldOfView(45.0, 45.0, 45.0, 45.0);
+  mDeviceInfo.mDeviceName.AssignLiteral("Phone Sensor (Cardboard) HMD");
 
-  SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0);
+  mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_Orientation;
 
-#if 1
-  int32_t xcoord = 0;
-  if (PR_GetEnv("FAKE_CARDBOARD_SCREEN")) {
-      const char *env = PR_GetEnv("FAKE_CARDBOARD_SCREEN");
-      nsresult err;
-      xcoord = nsCString(env).ToInteger(&err);
-      if (err != NS_OK) xcoord = 0;
-  }
-  mScreen = VRHMDManager::MakeFakeScreen(xcoord, 0, 1920, 1080);
-#endif
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0);
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0);
 
-}
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0);
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = gfx::VRFieldOfView(45.0, 45.0, 45.0, 45.0);
 
-bool
-HMDInfoCardboard::StartSensorTracking()
-{
-  LOG("HMDInfoCardboard::StartSensorTracking %d\n", mStartCount);
-  if (mStartCount == 0) {
-    // it's never been started before; initialize observers and
-    // initial state.
+  SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0);
 
-    mozilla::hal::ScreenConfiguration sconfig;
-    mozilla::hal::GetCurrentScreenConfiguration(&sconfig);
-    this->Notify(sconfig);
-
-    mozilla::hal::RegisterSensorObserver(mozilla::hal::SENSOR_GAME_ROTATION_VECTOR, this);
-    mozilla::hal::RegisterScreenConfigurationObserver(this);
-
-    mLastSensorState.Clear();
-  }
-
-  mStartCount++;
-  return true;
+  mDeviceInfo.mScreenRect.x = 0;
+  mDeviceInfo.mScreenRect.y = 0;
+  mDeviceInfo.mScreenRect.width = 1920;
+  mDeviceInfo.mScreenRect.height = 1080;
+  mDeviceInfo.mIsFakeScreen = true;
 }
 
-// Android sends us events that have a 90-degree rotation about 
-// the x axis compared to what we want (phone flat vs. phone held in front of the eyes).
-// Correct for this by applying a transform to undo this rotation.
-void
-HMDInfoCardboard::Notify(const mozilla::hal::ScreenConfiguration& config)
-{
-  mOrient = config.orientation();
-
-  if (mOrient == eScreenOrientation_LandscapePrimary) {
-    mScreenTransform = Quaternion(-0.5f, 0.5f, 0.5f, 0.5f);
-  } else if (mOrient == eScreenOrientation_LandscapeSecondary) {
-    mScreenTransform = Quaternion(-0.5f, -0.5f, -0.5f, 0.5f);
-  } else if (mOrient == eScreenOrientation_PortraitPrimary) {
-    mScreenTransform = Quaternion((float) -M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2);
-  } else if (mOrient == eScreenOrientation_PortraitSecondary) {
-    // Currently, PortraitSecondary event doesn't be triggered.
-    mScreenTransform = Quaternion((float) M_SQRT1_2, 0.f, 0.f, (float) M_SQRT1_2);
-  }
-}
-
-void
-HMDInfoCardboard::Notify(const mozilla::hal::SensorData& data)
-{
-  if (data.sensor() != mozilla::hal::SENSOR_GAME_ROTATION_VECTOR)
-    return;
-
-  const nsTArray<float>& sensorValues = data.values();
-
-  // This is super chatty
-  //LOG("HMDInfoCardboard::Notify %f %f %f %f\n", sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]);
-
-  mSavedLastSensor.Set(sensorValues[0], sensorValues[1], sensorValues[2], sensorValues[3]);
-  mSavedLastSensorTime = data.timestamp();
-  mNeedsSensorCompute = true;
-}
-
-void
-HMDInfoCardboard::ComputeStateFromLastSensor()
-{
-  if (!mNeedsSensorCompute)
-    return;
-
-  // apply the zero orientation
-  Quaternion q = mSensorZeroInverse * mSavedLastSensor;
-
-  // make a matrix from the quat
-  Matrix4x4 qm;
-  qm.SetRotationFromQuaternion(q);
-
-  // remap the coordinate space, based on the orientation
-  Matrix4x4 qmRemapped = RemapMatrixForOrientation(mOrient, qm);
-
-  // turn it back into a quat
-  q.SetFromRotationMatrix(qmRemapped);
-
-  // apply adjustment based on what's been done to the screen and the original zero
-  // position of the base coordinate space
-  q = mScreenTransform * q;
-
-  VRHMDSensorState& state = mLastSensorState;
-
-  state.flags |= State_Orientation;
-  state.orientation[0] = q.x;
-  state.orientation[1] = q.y;
-  state.orientation[2] = q.z;
-  state.orientation[3] = q.w;
-
-  state.timestamp = mSavedLastSensorTime / 1000000.0;
-
-  mNeedsSensorCompute = false;
-}
 
 VRHMDSensorState
 HMDInfoCardboard::GetSensorState(double timeOffset)
 {
-  ComputeStateFromLastSensor();
-  return mLastSensorState;
-}
-
-void
-HMDInfoCardboard::StopSensorTracking()
-{
-  LOG("HMDInfoCardboard::StopSensorTracking, count %d\n", mStartCount);
-  if (--mStartCount == 0) {
-    mozilla::hal::UnregisterScreenConfigurationObserver(this);
-    mozilla::hal::UnregisterSensorObserver(mozilla::hal::SENSOR_GAME_ROTATION_VECTOR, this);
-  }
+  // Actual sensor state is calculated on the main thread,
+  // within VRDeviceProxyOrientationFallBack
+  VRHMDSensorState result;
+  result.Clear();
+  return result;
 }
 
 void
 HMDInfoCardboard::ZeroSensor()
 {
-  mSensorZeroInverse = mSavedLastSensor;
-  mSensorZeroInverse.Invert();
+  MOZ_ASSERT(0, "HMDInfoCardboard::ZeroSensor not implemented.  "
+                "Should use VRDeviceProxyOrientationFallBack on main thread");
+}
+
+
+void
+HMDInfoCardboard::NotifyVsync(const TimeStamp& aVsyncTimestamp)
+{
+  // Nothing to do here for Cardboard VR
 }
 
 bool
-HMDInfoCardboard::SetFOV(const VRFieldOfView& aFOVLeft,
-                         const VRFieldOfView& aFOVRight,
+HMDInfoCardboard::KeepSensorTracking()
+{
+  // Nothing to do here for Cardboard VR
+  return true;
+}
+
+bool
+HMDInfoCardboard::SetFOV(const gfx::VRFieldOfView& aFOVLeft,
+                         const gfx::VRFieldOfView& aFOVRight,
                          double zNear, double zFar)
 {
   const float standardIPD = 0.064f;
 
-  for (uint32_t eye = 0; eye < NumEyes; eye++) {
-    mEyeFOV[eye] = eye == Eye_Left ? aFOVLeft : aFOVRight;
-    mEyeTranslation[eye] = Point3D(standardIPD * (eye == Eye_Left ? -1.0 : 1.0), 0.0, 0.0);
-    mEyeProjectionMatrix[eye] = mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
+  for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) {
+    mDeviceInfo.mEyeFOV[eye] = eye == VRDeviceInfo::Eye_Left ? aFOVLeft : aFOVRight;
+    mDeviceInfo.mEyeTranslation[eye] = Point3D(standardIPD * (eye == VRDeviceInfo::Eye_Left ? -1.0 : 1.0), 0.0, 0.0);
+    mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
 
     mDistortionMesh[eye].mVertices.SetLength(4);
     mDistortionMesh[eye].mIndices.SetLength(6);
 
     HMDInfoCardboard::DistortionVertex *destv = reinterpret_cast<HMDInfoCardboard::DistortionVertex*>(mDistortionMesh[eye].mVertices.Elements());
-    float xoffs = eye == Eye_Left ? 0.0f : 1.0f;
-    float txoffs = eye == Eye_Left ? 0.0f : 0.5f;
+    float xoffs = eye == VRDeviceInfo::Eye_Left ? 0.0f : 1.0f;
+    float txoffs = eye == VRDeviceInfo::Eye_Left ? 0.0f : 0.5f;
     destv[0].pos[0] = -1.0 + xoffs;
     destv[0].pos[1] = -1.0;
     destv[0].texR[0] = destv[0].texG[0] = destv[0].texB[0] = 0.0 + txoffs;
     destv[0].texR[1] = destv[0].texG[1] = destv[0].texB[1] = 1.0;
     destv[0].padding[0] = 1.0; // vignette factor
 
     destv[1].pos[0] = 0.0 + xoffs;
     destv[1].pos[1] = -1.0;
@@ -270,26 +124,26 @@ HMDInfoCardboard::SetFOV(const VRFieldOf
     destv[3].padding[0] = 1.0; // vignette factor
 
     uint16_t *iv = mDistortionMesh[eye].mIndices.Elements();
     iv[0] = 0; iv[1] = 1; iv[2] = 2;
     iv[3] = 2; iv[4] = 3; iv[5] = 0;
   }
 
   // XXX find out the default screen size and use that
-  mEyeResolution.width = 1920 / 2;
-  mEyeResolution.height = 1080;
+  mDeviceInfo.mEyeResolution.width = 1920 / 2;
+  mDeviceInfo.mEyeResolution.height = 1080;
 
   if (PR_GetEnv("FAKE_CARDBOARD_SCREEN")) {
     // for testing, make the eye resolution 2x of the screen
-    mEyeResolution.width *= 2;
-    mEyeResolution.height *= 2;
+    mDeviceInfo.mEyeResolution.width *= 2;
+    mDeviceInfo.mEyeResolution.height *= 2;
   }
 
-  mConfiguration.hmdType = mType;
+  mConfiguration.hmdType = mDeviceInfo.mType;
   mConfiguration.value = 0;
   mConfiguration.fov[0] = aFOVLeft;
   mConfiguration.fov[1] = aFOVRight;
 
   return true;
 }
 
 void
@@ -327,29 +181,36 @@ HMDInfoCardboard::FillDistortionConstant
   values.destinationScaleAndOffset[3] = destRect.height / destViewport.height;
 }
 
 void
 HMDInfoCardboard::Destroy()
 {
 }
 
-
+/*static*/ already_AddRefed<VRHMDManagerCardboard>
+VRHMDManagerCardboard::Create()
+{
+  MOZ_ASSERT(NS_IsMainThread());
 
-bool
-VRHMDManagerCardboard::PlatformInit()
-{
-  return gfxPrefs::VREnabled() && gfxPrefs::VRCardboardEnabled();
+  if (!gfxPrefs::VREnabled() || !gfxPrefs::VRCardboardEnabled())
+  {
+    return nullptr;
+  }
+
+  RefPtr<VRHMDManagerCardboard> manager = new VRHMDManagerCardboard();
+  return manager.forget();
 }
 
 bool
 VRHMDManagerCardboard::Init()
 {
-  if (mCardboardInitialized)
+  if (mCardboardInitialized) {
     return true;
+  }
 
   RefPtr<HMDInfoCardboard> hmd = new HMDInfoCardboard();
   mCardboardHMDs.AppendElement(hmd);
 
   mCardboardInitialized = true;
   return true;
 }
 
@@ -365,13 +226,16 @@ VRHMDManagerCardboard::Destroy()
 
   mCardboardHMDs.Clear();
   mCardboardInitialized = false;
 }
 
 void
 VRHMDManagerCardboard::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
 {
-  Init();
+  if (!mCardboardInitialized) {
+    return;
+  }
+
   for (size_t i = 0; i < mCardboardHMDs.Length(); ++i) {
     aHMDResult.AppendElement(mCardboardHMDs[i]);
   }
 }
--- a/gfx/vr/gfxVRCardboard.h
+++ b/gfx/vr/gfxVRCardboard.h
@@ -4,94 +4,74 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_VR_CARDBOARD_H
 #define GFX_VR_CARDBOARD_H
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Quaternion.h"
 #include "mozilla/EnumeratedArray.h"
-#include "mozilla/HalSensor.h"
-#include "mozilla/HalScreenConfiguration.h"
 
 #include "gfxVR.h"
 
 namespace mozilla {
 namespace gfx {
 namespace impl {
 
 class HMDInfoCardboard :
-    public VRHMDInfo,
-    public hal::ISensorObserver,
-    public hal::ScreenConfigurationObserver
+    public VRHMDInfo
 {
 public:
   explicit HMDInfoCardboard();
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar) override;
 
-  bool StartSensorTracking() override;
   VRHMDSensorState GetSensorState(double timeOffset) override;
-  void StopSensorTracking() override;
   void ZeroSensor() override;
+  bool KeepSensorTracking() override;
+  void NotifyVsync(const TimeStamp& aVsyncTimestamp) override;
 
   void FillDistortionConstants(uint32_t whichEye,
                                const IntSize& textureSize, const IntRect& eyeViewport,
                                const Size& destViewport, const Rect& destRect,
                                VRDistortionConstants& values) override;
 
   void Destroy();
 
-  // ISensorObserver interface
-  void Notify(const hal::SensorData& SensorData) override;
-  // ScreenConfigurationObserver interface
-  void Notify(const hal::ScreenConfiguration& ScreenConfiguration) override;
+protected:
+  virtual ~HMDInfoCardboard() {
+    MOZ_COUNT_DTOR_INHERITED(HMDInfoCardboard, VRHMDInfo);
+    Destroy();
+  }
 
-protected:
   // must match the size of VRDistortionVertex
   struct DistortionVertex {
     float pos[2];
     float texR[2];
     float texG[2];
     float texB[2];
     float padding[4];
   };
 
-  virtual ~HMDInfoCardboard() {
-    Destroy();
-  }
-
-  void ComputeStateFromLastSensor();
-
-  uint32_t mStartCount;
-  VRHMDSensorState mLastSensorState;
-  uint32_t mOrient;
-  Quaternion mScreenTransform;
-  Quaternion mSensorZeroInverse;
-
-  Quaternion mSavedLastSensor;
-  double mSavedLastSensorTime;
-  bool mNeedsSensorCompute;    // if we need to compute the state from mSavedLastSensor
 };
 
 } // namespace impl
 
 class VRHMDManagerCardboard : public VRHMDManager
 {
 public:
-  VRHMDManagerCardboard()
-    : mCardboardInitialized(false)
-  { }
-
-  virtual bool PlatformInit() override;
+  static already_AddRefed<VRHMDManagerCardboard> Create();
   virtual bool Init() override;
   virtual void Destroy() override;
   virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo> >& aHMDResult) override;
 protected:
+  VRHMDManagerCardboard()
+    : mCardboardInitialized(false)
+  { }
   nsTArray<RefPtr<impl::HMDInfoCardboard>> mCardboardHMDs;
   bool mCardboardInitialized;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_CARDBOARD_H */
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -6,28 +6,26 @@
 #include <math.h>
 
 #include "prlink.h"
 #include "prmem.h"
 #include "prenv.h"
 #include "gfxPrefs.h"
 #include "nsString.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
 
 #include "mozilla/gfx/Quaternion.h"
 
 #ifdef XP_WIN
 #include "../layers/d3d11/CompositorD3D11.h"
 #endif
 
 #include "gfxVROculus.h"
 
-#include "nsServiceManagerUtils.h"
-#include "nsIScreenManager.h"
-
 #ifndef M_PI
 # define M_PI 3.14159265358979323846
 #endif
 
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 
 namespace {
@@ -106,17 +104,17 @@ InitializeOculusCAPI()
     //libName.AppendPrintf("LibOVRRT_%d", OVR_PRODUCT_VERSION);
     libName.Append("LibOVRRT_");
     libName.AppendInt(OVR_PRODUCT_VERSION);
 #else
     libSearchPaths.AppendElement(nsCString("/usr/local/lib"));
     libSearchPaths.AppendElement(nsCString("/usr/lib"));
     libName.AppendPrintf("libOVRRT%d_%d.so.%d", BUILD_BITS, OVR_PRODUCT_VERSION, OVR_MAJOR_VERSION);
 #endif
-
+    
     // If the pref is present, we override libName
     nsAdoptingCString prefLibPath = mozilla::Preferences::GetCString("dom.vr.ovr_lib_path");
     if (prefLibPath && prefLibPath.get()) {
       libSearchPaths.InsertElementsAt(0, 1, prefLibPath);
     }
 
     nsAdoptingCString prefLibName = mozilla::Preferences::GetCString("dom.vr.ovr_lib_name");
     if (prefLibName && prefLibName.get()) {
@@ -222,120 +220,133 @@ do_CalcEyePoses(ovrPosef headPose,
     outEyePoses[i].Orientation = headPose.Orientation;
     outEyePoses[i].Position.x = p.x + headPose.Position.x;
     outEyePoses[i].Position.y = p.y + headPose.Position.y;
     outEyePoses[i].Position.z = p.z + headPose.Position.z;
   }
 }
 
 ovrFovPort
-ToFovPort(const VRFieldOfView& aFOV)
+ToFovPort(const gfx::VRFieldOfView& aFOV)
 {
   ovrFovPort fovPort;
   fovPort.LeftTan = tan(aFOV.leftDegrees * M_PI / 180.0);
   fovPort.RightTan = tan(aFOV.rightDegrees * M_PI / 180.0);
   fovPort.UpTan = tan(aFOV.upDegrees * M_PI / 180.0);
   fovPort.DownTan = tan(aFOV.downDegrees * M_PI / 180.0);
   return fovPort;
 }
 
-VRFieldOfView
+gfx::VRFieldOfView
 FromFovPort(const ovrFovPort& aFOV)
 {
-  VRFieldOfView fovInfo;
+  gfx::VRFieldOfView fovInfo;
   fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
   fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
   fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
   fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
   return fovInfo;
 }
 
 } // namespace
 
-HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD)
-  : VRHMDInfo(VRHMDType::Oculus)
+HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD, bool aDebug, int aDeviceID)
+  : VRHMDInfo(VRHMDType::Oculus, false)
   , mHMD(aHMD)
-  , mStartCount(0)
+  , mTracking(false)
+  , mDebug(aDebug)
+  , mDeviceID(aDeviceID)
+  , mSensorTrackingFramesRemaining(0)
 {
   MOZ_ASSERT(sizeof(HMDInfoOculus::DistortionVertex) == sizeof(VRDistortionVertex),
              "HMDInfoOculus::DistortionVertex must match the size of VRDistortionVertex");
 
   MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
 
-  mDeviceName.AssignLiteral("Oculus VR HMD");
-
-  mSupportedSensorBits = 0;
-  if (mHMD->TrackingCaps & ovrTrackingCap_Orientation)
-    mSupportedSensorBits |= State_Orientation;
-  if (mHMD->TrackingCaps & ovrTrackingCap_Position)
-    mSupportedSensorBits |= State_Position;
-
-  mRecommendedEyeFOV[Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
-  mRecommendedEyeFOV[Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
+  if (aDebug) {
+    mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD Debug)");
+  } else {
+    mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD");
+  }
 
-  mMaximumEyeFOV[Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
-  mMaximumEyeFOV[Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
-
-  SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0);
+  mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_None;
+  if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) {
+    mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Orientation;
+  }
+  if (mHMD->TrackingCaps & ovrTrackingCap_Position) {
+    mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Position;
+  }
 
-#if 1
-  int32_t xcoord = 0;
-  if (getenv("FAKE_OCULUS_SCREEN")) {
-      const char *env = getenv("FAKE_OCULUS_SCREEN");
-      nsresult err;
-      xcoord = nsCString(env).ToInteger(&err);
-      if (err != NS_OK) xcoord = 0;
-  }
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
+
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
+
   uint32_t w = mHMD->Resolution.w;
   uint32_t h = mHMD->Resolution.h;
-  mScreen = VRHMDManager::MakeFakeScreen(xcoord, 0, std::max(w, h), std::min(w, h));
+  mDeviceInfo.mScreenRect.x = 0;
+  mDeviceInfo.mScreenRect.y = 0;
+  mDeviceInfo.mScreenRect.width = std::max(w, h);
+  mDeviceInfo.mScreenRect.height = std::min(w, h);
+  mDeviceInfo.mIsFakeScreen = true;
+
+  SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0);
+}
 
-#ifdef DEBUG
-  printf_stderr("OCULUS SCREEN: %d %d %d %d\n", xcoord, 0, std::max(w, h), std::min(w, h));
-#endif
-#endif
+bool
+HMDInfoOculus::GetIsDebug() const
+{
+  return mDebug;
+}
+
+int
+HMDInfoOculus::GetDeviceID() const
+{
+  return mDeviceID;
 }
 
 void
 HMDInfoOculus::Destroy()
 {
+  StopSensorTracking();
   if (mHMD) {
     ovrHmd_Destroy(mHMD);
     mHMD = nullptr;
   }
 }
 
 bool
-HMDInfoOculus::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
+HMDInfoOculus::SetFOV(const gfx::VRFieldOfView& aFOVLeft, const gfx::VRFieldOfView& aFOVRight,
                       double zNear, double zFar)
 {
   float pixelsPerDisplayPixel = 1.0;
   ovrSizei texSize[2];
 
   // get eye parameters and create the mesh
-  for (uint32_t eye = 0; eye < NumEyes; eye++) {
-    mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
-    mFOVPort[eye] = ToFovPort(mEyeFOV[eye]);
+  for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) {
+    mDeviceInfo.mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
+    mFOVPort[eye] = ToFovPort(mDeviceInfo.mEyeFOV[eye]);
 
     ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]);
 
     // As of Oculus 0.6.0, the HmdToEyeViewOffset values are correct and don't need to be negated.
-    mEyeTranslation[eye] = Point3D(renderDesc.HmdToEyeViewOffset.x, renderDesc.HmdToEyeViewOffset.y, renderDesc.HmdToEyeViewOffset.z);
+    mDeviceInfo.mEyeTranslation[eye] = Point3D(renderDesc.HmdToEyeViewOffset.x, renderDesc.HmdToEyeViewOffset.y, renderDesc.HmdToEyeViewOffset.z);
 
     // note that we are using a right-handed coordinate system here, to match CSS
-    mEyeProjectionMatrix[eye] = mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
+    mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
 
     texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel);
   }
 
   // take the max of both for eye resolution
-  mEyeResolution.width = std::max(texSize[Eye_Left].w, texSize[Eye_Right].w);
-  mEyeResolution.height = std::max(texSize[Eye_Left].h, texSize[Eye_Right].h);
+  mDeviceInfo.mEyeResolution.width = std::max(texSize[VRDeviceInfo::Eye_Left].w, texSize[VRDeviceInfo::Eye_Right].w);
+  mDeviceInfo.mEyeResolution.height = std::max(texSize[VRDeviceInfo::Eye_Left].h, texSize[VRDeviceInfo::Eye_Right].h);
 
-  mConfiguration.hmdType = mType;
+  mConfiguration.hmdType = mDeviceInfo.mType;
   mConfiguration.value = 0;
   mConfiguration.fov[0] = aFOVLeft;
   mConfiguration.fov[1] = aFOVRight;
 
   return true;
 }
 
 void
@@ -344,33 +355,61 @@ HMDInfoOculus::FillDistortionConstants(u
                                        const IntRect& eyeViewport,
                                        const Size& destViewport,
                                        const Rect& destRect,
                                        VRDistortionConstants& values)
 {
 }
 
 bool
+HMDInfoOculus::KeepSensorTracking()
+{
+  // Keep sensor tracking alive for short time after the last request for
+  // tracking state by content.  Value conservatively high to accomodate
+  // potentially high frame rates.
+  const uint32_t kKeepAliveFrames = 200;
+
+  bool success = true;
+  if (mSensorTrackingFramesRemaining == 0) {
+    success = StartSensorTracking();
+  }
+  if (success) {
+    mSensorTrackingFramesRemaining = kKeepAliveFrames;
+  }
+
+  return success;
+}
+
+void
+HMDInfoOculus::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp)
+{
+  if (mSensorTrackingFramesRemaining == 1) {
+    StopSensorTracking();
+  }
+  if (mSensorTrackingFramesRemaining) {
+    --mSensorTrackingFramesRemaining;
+  }
+}
+
+bool
 HMDInfoOculus::StartSensorTracking()
 {
-  if (mStartCount == 0) {
-    bool ok = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
-    if (!ok)
-      return false;
+  if (!mTracking) {
+    mTracking = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
   }
 
-  mStartCount++;
-  return true;
+  return mTracking;
 }
 
 void
 HMDInfoOculus::StopSensorTracking()
 {
-  if (--mStartCount == 0) {
+  if (mTracking) {
     ovrHmd_ConfigureTracking(mHMD, 0, 0);
+    mTracking = false;
   }
 }
 
 void
 HMDInfoOculus::ZeroSensor()
 {
   ovrHmd_RecenterPose(mHMD);
 }
@@ -384,34 +423,34 @@ HMDInfoOculus::GetSensorState(double tim
   // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
   // the Oculus time base and the browser one.
   ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
-    result.flags |= State_Orientation;
+    result.flags |= VRStateValidFlags::State_Orientation;
 
     result.orientation[0] = pose.ThePose.Orientation.x;
     result.orientation[1] = pose.ThePose.Orientation.y;
     result.orientation[2] = pose.ThePose.Orientation.z;
     result.orientation[3] = pose.ThePose.Orientation.w;
     
     result.angularVelocity[0] = pose.AngularVelocity.x;
     result.angularVelocity[1] = pose.AngularVelocity.y;
     result.angularVelocity[2] = pose.AngularVelocity.z;
 
     result.angularAcceleration[0] = pose.AngularAcceleration.x;
     result.angularAcceleration[1] = pose.AngularAcceleration.y;
     result.angularAcceleration[2] = pose.AngularAcceleration.z;
   }
 
   if (state.StatusFlags & ovrStatus_PositionTracked) {
-    result.flags |= State_Position;
+    result.flags |= VRStateValidFlags::State_Position;
 
     result.position[0] = pose.ThePose.Position.x;
     result.position[1] = pose.ThePose.Position.y;
     result.position[2] = pose.ThePose.Position.z;
     
     result.linearVelocity[0] = pose.LinearVelocity.x;
     result.linearVelocity[1] = pose.LinearVelocity.y;
     result.linearVelocity[2] = pose.LinearVelocity.z;
@@ -556,114 +595,146 @@ HMDInfoOculus::SubmitFrame(RenderTargetS
   layer.Viewport[0].Pos.y = 0;
   layer.Viewport[0].Size.w = rts->size.width / 2;
   layer.Viewport[0].Size.h = rts->size.height;
   layer.Viewport[1].Pos.x = rts->size.width / 2;
   layer.Viewport[1].Pos.y = 0;
   layer.Viewport[1].Size.w = rts->size.width / 2;
   layer.Viewport[1].Size.h = rts->size.height;
 
-  const Point3D& l = rts->hmd->mEyeTranslation[0];
-  const Point3D& r = rts->hmd->mEyeTranslation[1];
+  const Point3D& l = rts->hmd->mDeviceInfo.mEyeTranslation[0];
+  const Point3D& r = rts->hmd->mDeviceInfo.mEyeTranslation[1];
   const ovrVector3f hmdToEyeViewOffset[2] = { { l.x, l.y, l.z },
                                               { r.x, r.y, r.z } };
   do_CalcEyePoses(rts->hmd->mLastTrackingState.HeadPose.ThePose, hmdToEyeViewOffset, layer.RenderPose);
 
   ovrLayerHeader *layers = &layer.Header;
   ovrResult orv = ovrHmd_SubmitFrame(mHMD, 0, nullptr, &layers, 1);
   //printf_stderr("Submitted frame %d, result: %d\n", rts->textureSet->CurrentIndex, orv);
   if (orv != ovrSuccess) {
     // not visible? failed?
   }
 }
 
-bool
-VRHMDManagerOculus::PlatformInit()
+/*static*/ already_AddRefed<VRHMDManagerOculus>
+VRHMDManagerOculus::Create()
 {
-  if (mOculusPlatformInitialized)
-    return true;
+  MOZ_ASSERT(NS_IsMainThread());
 
-  if (!gfxPrefs::VREnabled() ||
-      !gfxPrefs::VROculusEnabled())
+  if (!gfxPrefs::VREnabled() || !gfxPrefs::VROculusEnabled())
   {
-    return false;
+    return nullptr;
   }
 
-  if (!InitializeOculusCAPI())
-    return false;
+  if (!InitializeOculusCAPI()) {
+    return nullptr;
+  }
 
-  ovrInitParams params;
-  params.Flags = ovrInit_RequestVersion;
-  params.RequestedMinorVersion = OVR_MINOR_VERSION;
-  params.LogCallback = nullptr;
-  params.ConnectionTimeoutMS = 0;
-
-  ovrResult orv = ovr_Initialize(&params);
-
-  if (orv != ovrSuccess)
-    return false;
-
-  mOculusPlatformInitialized = true;
-  return true;
+  RefPtr<VRHMDManagerOculus> manager = new VRHMDManagerOculus();
+  return manager.forget();
 }
 
 bool
 VRHMDManagerOculus::Init()
 {
-  if (mOculusInitialized)
-    return true;
+  if (!mOculusInitialized) {
+    nsIThread* thread = nullptr;
+    NS_GetCurrentThread(&thread);
+    mOculusThread = already_AddRefed<nsIThread>(thread);
+
+    ovrInitParams params;
+    params.Flags = ovrInit_RequestVersion;
+    params.RequestedMinorVersion = OVR_MINOR_VERSION;
+    params.LogCallback = nullptr;
+    params.ConnectionTimeoutMS = 0;
+
+    ovrResult orv = ovr_Initialize(&params);
+
+    if (orv == ovrSuccess) {
+      mOculusInitialized = true;
+    }
+  }
+
+  return mOculusInitialized;
+}
 
-  if (!PlatformInit())
-    return false;
+void
+VRHMDManagerOculus::Destroy()
+{
+  if(mOculusInitialized) {
+    MOZ_ASSERT(NS_GetCurrentThread() == mOculusThread);
+    mOculusThread = nullptr;
+
+    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
+      mOculusHMDs[i]->Destroy();
+    }
+
+    mOculusHMDs.Clear();
+
+    ovr_Shutdown();
+    mOculusInitialized = false;
+  }
+}
+
+void
+VRHMDManagerOculus::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
+{
+  if (!mOculusInitialized) {
+    return;
+  }
+
+  nsTArray<RefPtr<impl::HMDInfoOculus> > newHMDs;
 
   ovrResult orv;
+
   int count = ovrHmd_Detect();
-  
-  for (int i = 0; i < count; ++i) {
-    ovrHmd hmd;
-    orv = ovrHmd_Create(i, &hmd);
-    if (orv == ovrSuccess) {
-      RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
-      mOculusHMDs.AppendElement(oc);
+
+  for (int j = 0; j < count; ++j) {
+    bool is_new = true;
+    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
+      if (mOculusHMDs[i]->GetDeviceID() == j) {
+        newHMDs.AppendElement(mOculusHMDs[i]);
+        is_new = false;
+        break;
+      }
+    }
+
+    if (is_new) {
+      ovrHmd hmd;
+      orv = ovrHmd_Create(j, &hmd);
+      if (orv == ovrSuccess) {
+        RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd, false, j);
+        newHMDs.AppendElement(oc);
+      }
     }
   }
 
   // VRAddTestDevices == 1: add test device only if no real devices present
   // VRAddTestDevices == 2: add test device always
   if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) ||
-      (gfxPrefs::VRAddTestDevices() == 2))
+    (gfxPrefs::VRAddTestDevices() == 2))
   {
-    ovrHmd hmd;
-    orv = ovrHmd_CreateDebug(ovrHmd_DK2, &hmd);
-    if (orv == ovrSuccess) {
-      RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd);
-      mOculusHMDs.AppendElement(oc);
+    // Keep existing debug HMD if possible
+    bool foundDebug = false;
+    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
+      if (mOculusHMDs[i]->GetIsDebug()) {
+        newHMDs.AppendElement(mOculusHMDs[i]);
+        foundDebug = true;
+      }
+    }
+
+    // If there isn't already a debug HMD, create one
+    if (!foundDebug) {
+      ovrHmd hmd;
+      orv = ovrHmd_CreateDebug(ovrHmd_DK2, &hmd);
+      if (orv == ovrSuccess) {
+        RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd, true, -1);
+        newHMDs.AppendElement(oc);
+      }
     }
   }
 
-  mOculusInitialized = true;
-  return true;
-}
-
-void
-VRHMDManagerOculus::Destroy()
-{
-  if (!mOculusInitialized)
-    return;
+  mOculusHMDs = newHMDs;
 
-  for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-    mOculusHMDs[i]->Destroy();
-  }
-
-  mOculusHMDs.Clear();
-
-  ovr_Shutdown();
-  mOculusInitialized = false;
-}
-
-void
-VRHMDManagerOculus::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
-{
-  Init();
-  for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-    aHMDResult.AppendElement(mOculusHMDs[i]);
+  for (size_t j = 0; j < mOculusHMDs.Length(); ++j) {
+    aHMDResult.AppendElement(mOculusHMDs[j]);
   }
 }
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -2,96 +2,104 @@
  * 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_OCULUS_H
 #define GFX_VR_OCULUS_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 <OVR_CAPI.h>
 //#include <OVR_CAPI_D3D.h>
 #include "ovr_capi_dynamic.h"
 
 namespace mozilla {
 namespace gfx {
 namespace impl {
 
 class HMDInfoOculus : public VRHMDInfo, public VRHMDRenderingSupport {
 public:
-  explicit HMDInfoOculus(ovrHmd aHMD);
+  explicit HMDInfoOculus(ovrHmd aHMD, bool aDebug, int aDeviceID);
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar) override;
 
-  bool StartSensorTracking() override;
   VRHMDSensorState GetSensorState(double timeOffset) override;
-  void StopSensorTracking() override;
   void ZeroSensor() override;
+  bool KeepSensorTracking() override;
+  void NotifyVsync(const TimeStamp& aVsyncTimestamp) override;
 
   void FillDistortionConstants(uint32_t whichEye,
                                const IntSize& textureSize, const IntRect& eyeViewport,
                                const Size& destViewport, const Rect& destRect,
                                VRDistortionConstants& values) override;
 
   VRHMDRenderingSupport* GetRenderingSupport() override { return this; }
   
   void Destroy();
 
   /* VRHMDRenderingSupport */
   already_AddRefed<RenderTargetSet> CreateRenderTargetSet(layers::Compositor *aCompositor, const IntSize& aSize) override;
   void DestroyRenderTargetSet(RenderTargetSet *aRTSet) override;
   void SubmitFrame(RenderTargetSet *aRTSet) override;
 
   ovrHmd GetOculusHMD() const { return mHMD; }
+  bool GetIsDebug() const;
+  int GetDeviceID() const;
 
 protected:
+  virtual ~HMDInfoOculus() {
+      Destroy();
+      MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
+  }
+
+  bool StartSensorTracking();
+  void StopSensorTracking();
+
   // must match the size of VRDistortionVertex
   struct DistortionVertex {
     float pos[2];
     float texR[2];
     float texG[2];
     float texB[2];
     float genericAttribs[4];
   };
 
-  virtual ~HMDInfoOculus() {
-      Destroy();
-      MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
-  }
-
   ovrHmd mHMD;
   ovrFovPort mFOVPort[2];
-  uint32_t mStartCount;
+  bool mTracking;
   ovrTrackingState mLastTrackingState;
+
+  bool mDebug; // True if this is a debug HMD
+  int mDeviceID; // Index of device passed to ovrHmd_Create
+
+  uint32_t mSensorTrackingFramesRemaining;
 };
 
 } // namespace impl
 
 class VRHMDManagerOculus : public VRHMDManager
 {
 public:
-  VRHMDManagerOculus()
-    : mOculusInitialized(false), mOculusPlatformInitialized(false)
-  { }
-
-  virtual bool PlatformInit() override;
+  static already_AddRefed<VRHMDManagerOculus> Create();
   virtual bool Init() override;
   virtual void Destroy() override;
   virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo> >& aHMDResult) override;
 protected:
+  VRHMDManagerOculus()
+    : mOculusInitialized(false)
+  { }
+
   nsTArray<RefPtr<impl::HMDInfoOculus>> mOculusHMDs;
   bool mOculusInitialized;
-  bool mOculusPlatformInitialized;
+  RefPtr<nsIThread> mOculusThread;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_OCULUS_H */
--- a/gfx/vr/gfxVROculus050.cpp
+++ b/gfx/vr/gfxVROculus050.cpp
@@ -9,22 +9,20 @@
 #endif
 
 #include "prlink.h"
 #include "prmem.h"
 #include "prenv.h"
 #include "gfxPrefs.h"
 #include "nsString.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
 
 #include "gfxVROculus050.h"
 
-#include "nsServiceManagerUtils.h"
-#include "nsIScreenManager.h"
-
 #ifndef M_PI
 # define M_PI 3.14159265358979323846
 #endif
 
 using namespace ovr050;
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 
@@ -235,92 +233,103 @@ FromFovPort(const ovrFovPort& aFOV)
   fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
   fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
   fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
   return fovInfo;
 }
 
 } // anonymous namespace
 
-HMDInfoOculus050::HMDInfoOculus050(ovrHmd aHMD)
-  : VRHMDInfo(VRHMDType::Oculus050)
+HMDInfoOculus050::HMDInfoOculus050(ovrHmd aHMD, bool aDebug, int aDeviceID)
+  : VRHMDInfo(VRHMDType::Oculus050, false)
   , mHMD(aHMD)
-  , mStartCount(0)
+  , mTracking(false)
+  , mDebug(aDebug)
+  , mDeviceID(aDeviceID)
+  , mSensorTrackingFramesRemaining(0)
 {
   MOZ_ASSERT(sizeof(HMDInfoOculus050::DistortionVertex) == sizeof(VRDistortionVertex),
              "HMDInfoOculus050::DistortionVertex must match the size of VRDistortionVertex");
 
   MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus050, VRHMDInfo);
 
-  mDeviceName.AssignLiteral("Oculus VR HMD (0.5.0)");
-
-  mSupportedSensorBits = 0;
-  if (mHMD->TrackingCaps & ovrTrackingCap_Orientation)
-    mSupportedSensorBits |= State_Orientation;
-  if (mHMD->TrackingCaps & ovrTrackingCap_Position)
-    mSupportedSensorBits |= State_Position;
+  if (aDebug) {
+    mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD (0.5.0 Debug)");
+  } else {
+    mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD (0.5.0)");
+  }
 
-  mRecommendedEyeFOV[Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
-  mRecommendedEyeFOV[Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
+  mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_None;
+  if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) {
+    mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Orientation;
+  }
+  if (mHMD->TrackingCaps & ovrTrackingCap_Position) {
+    mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Position;
+  }
 
-  mMaximumEyeFOV[Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
-  mMaximumEyeFOV[Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
 
-  SetFOV(mRecommendedEyeFOV[Eye_Left], mRecommendedEyeFOV[Eye_Right], 0.01, 10000.0);
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
 
-  nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
-  if (screenmgr) {
-#if 1
-    if (getenv("FAKE_OCULUS_SCREEN")) {
-      const char *env = getenv("FAKE_OCULUS_SCREEN");
-      nsresult err;
-      int32_t xcoord = nsCString(env).ToInteger(&err);
-      if (err != NS_OK) xcoord = 0;
-      mScreen = VRHMDManager::MakeFakeScreen(xcoord, 0, 1920, 1080);
-    } else
-#endif
-    {
-      screenmgr->ScreenForRect(mHMD->WindowsPos.x, mHMD->WindowsPos.y,
-                               mHMD->Resolution.w, mHMD->Resolution.h,
-                               getter_AddRefs(mScreen));
-    }
-  }
+  mDeviceInfo.mScreenRect.x = mHMD->WindowsPos.x;
+  mDeviceInfo.mScreenRect.y = mHMD->WindowsPos.y;
+  mDeviceInfo.mScreenRect.width = mHMD->Resolution.w;
+  mDeviceInfo.mScreenRect.height = mHMD->Resolution.h;
+  mDeviceInfo.mIsFakeScreen = false;
+
+  SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0);
+}
+
+bool
+HMDInfoOculus050::GetIsDebug() const
+{
+  return mDebug;
+}
+
+int
+HMDInfoOculus050::GetDeviceID() const
+{
+  return mDeviceID;
 }
 
 void
 HMDInfoOculus050::Destroy()
 {
+  StopSensorTracking();
+
   if (mHMD) {
     ovrHmd_Destroy(mHMD);
     mHMD = nullptr;
   }
 }
 
 bool
 HMDInfoOculus050::SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
                       double zNear, double zFar)
 {
   float pixelsPerDisplayPixel = 1.0;
   ovrSizei texSize[2];
 
   uint32_t caps = ovrDistortionCap_Chromatic | ovrDistortionCap_Vignette; // XXX TODO add TimeWarp
 
   // get eye parameters and create the mesh
-  for (uint32_t eye = 0; eye < NumEyes; eye++) {
-    mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
-    mFOVPort[eye] = ToFovPort(mEyeFOV[eye]);
+  for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) {
+    mDeviceInfo.mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
+    mFOVPort[eye] = ToFovPort(mDeviceInfo.mEyeFOV[eye]);
 
     ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]);
 
     // these values are negated so that content can add the adjustment to its camera position,
     // instead of subtracting
-    mEyeTranslation[eye] = Point3D(-renderDesc.HmdToEyeViewOffset.x, -renderDesc.HmdToEyeViewOffset.y, -renderDesc.HmdToEyeViewOffset.z);
+    mDeviceInfo.mEyeTranslation[eye] = Point3D(-renderDesc.HmdToEyeViewOffset.x, -renderDesc.HmdToEyeViewOffset.y, -renderDesc.HmdToEyeViewOffset.z);
 
     // note that we are using a right-handed coordinate system here, to match CSS
-    mEyeProjectionMatrix[eye] = mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
+    mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
 
     texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel);
 
     ovrDistortionMesh mesh;
     bool ok = ovrHmd_CreateDistortionMesh(mHMD, (ovrEyeType) eye, mFOVPort[eye], caps, &mesh);
     if (!ok)
       return false;
 
@@ -345,20 +354,20 @@ HMDInfoOculus050::SetFOV(const VRFieldOf
       destv[i].genericAttribs[1] = srcv[i].TimeWarpFactor;
     }
 
     memcpy(mDistortionMesh[eye].mIndices.Elements(), mesh.pIndexData, mesh.IndexCount * sizeof(uint16_t));
     ovrHmd_DestroyDistortionMesh(&mesh);
   }
 
   // take the max of both for eye resolution
-  mEyeResolution.width = std::max(texSize[Eye_Left].w, texSize[Eye_Right].w);
-  mEyeResolution.height = std::max(texSize[Eye_Left].h, texSize[Eye_Right].h);
+  mDeviceInfo.mEyeResolution.width = std::max(texSize[VRDeviceInfo::Eye_Left].w, texSize[VRDeviceInfo::Eye_Right].w);
+  mDeviceInfo.mEyeResolution.height = std::max(texSize[VRDeviceInfo::Eye_Left].h, texSize[VRDeviceInfo::Eye_Right].h);
 
-  mConfiguration.hmdType = mType;
+  mConfiguration.hmdType = mDeviceInfo.mType;
   mConfiguration.value = 0;
   mConfiguration.fov[0] = aFOVLeft;
   mConfiguration.fov[1] = aFOVRight;
 
   return true;
 }
 
 void
@@ -394,33 +403,61 @@ HMDInfoOculus050::FillDistortionConstant
   values.destinationScaleAndOffset[0] = (x0+x1) / 2.0;
   values.destinationScaleAndOffset[1] = (y0+y1) / 2.0;
   // scale
   values.destinationScaleAndOffset[2] = destRect.width / destViewport.width;
   values.destinationScaleAndOffset[3] = destRect.height / destViewport.height;
 }
 
 bool
+HMDInfoOculus050::KeepSensorTracking()
+{
+  // Keep sensor tracking alive for short time after the last request for
+  // tracking state by content.  Value conservatively high to accomodate
+  // potentially high frame rates.
+  const uint32_t kKeepAliveFrames = 200;
+
+  bool success = true;
+  if (mSensorTrackingFramesRemaining == 0) {
+    success = StartSensorTracking();
+  }
+  if (success) {
+    mSensorTrackingFramesRemaining = kKeepAliveFrames;
+  }
+
+  return success;
+}
+
+void
+HMDInfoOculus050::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp)
+{
+  if (mSensorTrackingFramesRemaining == 1) {
+    StopSensorTracking();
+  }
+  if (mSensorTrackingFramesRemaining) {
+    --mSensorTrackingFramesRemaining;
+  }
+}
+
+bool
 HMDInfoOculus050::StartSensorTracking()
 {
-  if (mStartCount == 0) {
-    bool ok = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
-    if (!ok)
-      return false;
+  if (!mTracking) {
+    mTracking = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
   }
 
-  mStartCount++;
-  return true;
+  return mTracking;
 }
 
 void
 HMDInfoOculus050::StopSensorTracking()
 {
-  if (--mStartCount == 0) {
+  if (mTracking) {
     ovrHmd_ConfigureTracking(mHMD, 0, 0);
+    mTracking = false;
   }
 }
 
 void
 HMDInfoOculus050::ZeroSensor()
 {
   ovrHmd_RecenterPose(mHMD);
 }
@@ -434,34 +471,34 @@ HMDInfoOculus050::GetSensorState(double 
   // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
   // the Oculus time base and the browser one.
   ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
-    result.flags |= State_Orientation;
+    result.flags |= VRStateValidFlags::State_Orientation;
 
     result.orientation[0] = pose.ThePose.Orientation.x;
     result.orientation[1] = pose.ThePose.Orientation.y;
     result.orientation[2] = pose.ThePose.Orientation.z;
     result.orientation[3] = pose.ThePose.Orientation.w;
     
     result.angularVelocity[0] = pose.AngularVelocity.x;
     result.angularVelocity[1] = pose.AngularVelocity.y;
     result.angularVelocity[2] = pose.AngularVelocity.z;
 
     result.angularAcceleration[0] = pose.AngularAcceleration.x;
     result.angularAcceleration[1] = pose.AngularAcceleration.y;
     result.angularAcceleration[2] = pose.AngularAcceleration.z;
   }
 
   if (state.StatusFlags & ovrStatus_PositionTracked) {
-    result.flags |= State_Position;
+    result.flags |= VRStateValidFlags::State_Position;
 
     result.position[0] = pose.ThePose.Position.x;
     result.position[1] = pose.ThePose.Position.y;
     result.position[2] = pose.ThePose.Position.z;
     
     result.linearVelocity[0] = pose.LinearVelocity.x;
     result.linearVelocity[1] = pose.LinearVelocity.y;
     result.linearVelocity[2] = pose.LinearVelocity.z;
@@ -469,97 +506,128 @@ HMDInfoOculus050::GetSensorState(double 
     result.linearAcceleration[0] = pose.LinearAcceleration.x;
     result.linearAcceleration[1] = pose.LinearAcceleration.y;
     result.linearAcceleration[2] = pose.LinearAcceleration.z;
   }
 
   return result;
 }
 
-bool
-VRHMDManagerOculus050::PlatformInit()
+/*static*/ already_AddRefed<VRHMDManagerOculus050>
+VRHMDManagerOculus050::Create()
 {
-  if (mOculusPlatformInitialized)
-    return true;
+  MOZ_ASSERT(NS_IsMainThread());
 
-  if (!gfxPrefs::VREnabled() ||
-      !gfxPrefs::VROculus050Enabled())
+  if (!gfxPrefs::VREnabled() || !gfxPrefs::VROculus050Enabled())
   {
-    return false;
+    return nullptr;
   }
 
-  if (!InitializeOculusCAPI())
-    return false;
+  if (!InitializeOculusCAPI()) {
+    return nullptr;
+  }
 
-  ovrInitParams params;
-  params.Flags = ovrInit_RequestVersion;
-  params.RequestedMinorVersion = LIBOVR_MINOR_VERSION;
-  params.LogCallback = nullptr;
-  params.ConnectionTimeoutMS = 0;
-
-  bool ok = ovr_Initialize(&params);
-
-  if (!ok)
-    return false;
-
-  mOculusPlatformInitialized = true;
-  return true;
+  RefPtr<VRHMDManagerOculus050> manager = new VRHMDManagerOculus050();
+  return manager.forget();
 }
 
 bool
 VRHMDManagerOculus050::Init()
 {
-  if (mOculusInitialized)
-    return true;
+  if (!mOculusInitialized) {
+    nsIThread* thread = nullptr;
+    NS_GetCurrentThread(&thread);
+    mOculusThread = already_AddRefed<nsIThread>(thread);
+
+    ovrInitParams params;
+    params.Flags = ovrInit_RequestVersion;
+    params.RequestedMinorVersion = LIBOVR_MINOR_VERSION;
+    params.LogCallback = nullptr;
+    params.ConnectionTimeoutMS = 0;
+
+    bool ok = ovr_Initialize(&params);
+
+    if (ok) {
+      mOculusInitialized = true;
+    }
+  }
+  return mOculusInitialized;
+}
 
-  if (!PlatformInit())
-    return false;
+void
+VRHMDManagerOculus050::Destroy()
+{
+  if (mOculusInitialized) {
+    MOZ_ASSERT(NS_GetCurrentThread() == mOculusThread);
+    mOculusThread = nullptr;
+
+    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
+      mOculusHMDs[i]->Destroy();
+    }
+
+    mOculusHMDs.Clear();
+
+    ovr_Shutdown();
+
+    mOculusInitialized = false;
+  }
+}
+
+void
+VRHMDManagerOculus050::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
+{
+  if (!mOculusInitialized) {
+    return;
+  }
+
+  nsTArray<RefPtr<impl::HMDInfoOculus050> > newHMDs;
 
   int count = ovrHmd_Detect();
 
-  for (int i = 0; i < count; ++i) {
-    ovrHmd hmd = ovrHmd_Create(i);
-    if (hmd) {
-      RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd);
-      mOculusHMDs.AppendElement(oc);
+  for (int j = 0; j < count; ++j) {
+    bool is_new = true;
+    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
+      if(mOculusHMDs[i]->GetDeviceID() == j) {
+        newHMDs.AppendElement(mOculusHMDs[i]);
+        is_new = false;
+        break;
+      }
+    }
+
+    if(is_new) {
+      ovrHmd hmd = ovrHmd_Create(j);
+      if (hmd) {
+        RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd, false, j);
+        newHMDs.AppendElement(oc);
+      }
     }
   }
 
   // VRAddTestDevices == 1: add test device only if no real devices present
   // VRAddTestDevices == 2: add test device always
   if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) ||
       (gfxPrefs::VRAddTestDevices() == 2))
   {
-    ovrHmd hmd = ovrHmd_CreateDebug(ovrHmd_DK2);
-    if (hmd) {
-      RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd);
-      mOculusHMDs.AppendElement(oc);
+    // Keep existing debug HMD if possible
+    bool foundDebug = false;
+    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
+      if (mOculusHMDs[i]->GetIsDebug()) {
+        newHMDs.AppendElement(mOculusHMDs[i]);
+        foundDebug = true;
+      }
+    }
+
+    // If there isn't already a debug HMD, create one
+    if (!foundDebug) {
+      ovrHmd hmd = ovrHmd_CreateDebug(ovrHmd_DK2);
+      if (hmd) {
+        RefPtr<HMDInfoOculus050> oc = new HMDInfoOculus050(hmd, true, -1);
+        newHMDs.AppendElement(oc);
+      }
     }
   }
 
-  mOculusInitialized = true;
-  return true;
-}
-
-void
-VRHMDManagerOculus050::Destroy()
-{
-  if (!mOculusInitialized)
-    return;
+  mOculusHMDs = newHMDs;
 
-  for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-    mOculusHMDs[i]->Destroy();
-  }
-
-  mOculusHMDs.Clear();
-
-  ovr_Shutdown();
-  mOculusInitialized = false;
-}
-
-void
-VRHMDManagerOculus050::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
-{
-  Init();
-  for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-    aHMDResult.AppendElement(mOculusHMDs[i]);
+  for (size_t j = 0; j < mOculusHMDs.Length(); ++j) {
+    aHMDResult.AppendElement(mOculusHMDs[j]);
   }
 }
--- a/gfx/vr/gfxVROculus050.h
+++ b/gfx/vr/gfxVROculus050.h
@@ -2,84 +2,91 @@
  * 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_OCULUS_050_H
 #define GFX_VR_OCULUS_050_H
 
 #include "nsTArray.h"
-#include "nsIScreen.h"
-#include "nsCOMPtr.h"
+#include "nsThreadUtils.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 
-#include "mozilla/gfx/2D.h"
-#include "mozilla/EnumeratedArray.h"
-
 #include "gfxVR.h"
 #include "ovr_capi_dynamic050.h"
 
 namespace mozilla {
 namespace gfx {
 namespace impl {
 
 class HMDInfoOculus050 : public VRHMDInfo {
 public:
-  explicit HMDInfoOculus050(ovr050::ovrHmd aHMD);
+  explicit HMDInfoOculus050(ovr050::ovrHmd aHMD, bool aDebug, int aDeviceID);
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar) override;
 
-  bool StartSensorTracking() override;
   VRHMDSensorState GetSensorState(double timeOffset) override;
-  void StopSensorTracking() override;
   void ZeroSensor() override;
+  bool KeepSensorTracking() override;
+  void NotifyVsync(const TimeStamp& aVsyncTimestamp) override;
 
   void FillDistortionConstants(uint32_t whichEye,
                                const IntSize& textureSize, const IntRect& eyeViewport,
                                const Size& destViewport, const Rect& destRect,
                                VRDistortionConstants& values) override;
 
   void Destroy();
+  bool GetIsDebug() const;
+  int GetDeviceID() const;
 
 protected:
+  virtual ~HMDInfoOculus050() {
+      Destroy();
+      MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus050, VRHMDInfo);
+  }
+
+  bool StartSensorTracking();
+  void StopSensorTracking();
+
   // must match the size of VRDistortionVertex
   struct DistortionVertex {
     float pos[2];
     float texR[2];
     float texG[2];
     float texB[2];
     float genericAttribs[4];
   };
 
-  virtual ~HMDInfoOculus050() {
-      Destroy();
-      MOZ_COUNT_DTOR_INHERITED(HMDInfoOculus050, VRHMDInfo);
-  }
-
   ovr050::ovrHmd mHMD;
   ovr050::ovrFovPort mFOVPort[2];
-  uint32_t mStartCount;
+  uint32_t mTracking;
+  bool mDebug; // True if this is a debug HMD
+  int mDeviceID; // ID of device passed to ovrHmd_Create
+
+  uint32_t mSensorTrackingFramesRemaining;
 };
 
 } // namespace impl
 
 class VRHMDManagerOculus050 : public VRHMDManager
 {
 public:
-  VRHMDManagerOculus050()
-    : mOculusInitialized(false), mOculusPlatformInitialized(false)
-  { }
-
-  virtual bool PlatformInit() override;
+  static already_AddRefed<VRHMDManagerOculus050> Create();
   virtual bool Init() override;
   virtual void Destroy() override;
   virtual void GetHMDs(nsTArray<RefPtr<VRHMDInfo> >& aHMDResult) override;
 protected:
-  nsTArray<RefPtr<impl::HMDInfoOculus050>> mOculusHMDs;
+  VRHMDManagerOculus050()
+    : mOculusInitialized(false)
+  { }
+
+  nsTArray<RefPtr<impl::HMDInfoOculus050> > mOculusHMDs;
   bool mOculusInitialized;
-  bool mOculusPlatformInitialized;
+  RefPtr<nsIThread> mOculusThread;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_OCULUS_050_H */
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/PVRManager.ipdl
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "VRMessageUtils.h";
+
+using struct mozilla::gfx::VRFieldOfView from "gfxVR.h";
+using struct mozilla::gfx::VRDeviceUpdate from "gfxVR.h";
+using struct mozilla::gfx::VRSensorUpdate from "gfxVR.h";
+
+namespace mozilla {
+namespace gfx {
+
+/**
+ * The PVRManager protocol is used to enable communication of VR device
+ * enumeration and sensor state between the compositor thread and
+ * content threads/processes.
+ */
+sync protocol PVRManager
+{
+parent:
+  // (Re)Enumerate VR Devices.  An updated list of VR devices will be returned
+  // asynchronously to children via UpdateDeviceInfo.
+  async RefreshDevices();
+
+  // Reset the sensor of the device identified by aDeviceID so that the current
+  // sensor state is the "Zero" position.
+  async ResetSensor(uint32_t aDeviceID);
+
+  // KeepSensorTracking is called continuously by children to indicate their
+  // interest in receiving sensor data from the device identified by aDeviceID.
+  // This will activate any physical sensor tracking system requiring
+  // initialization and guarantee that it will remain active until at least one
+  // second has passed since the last KeepSensorTracking call has been made.
+  // Sensor data will be sent asynchronously via UpdateDeviceSensors
+  async KeepSensorTracking(uint32_t aDeviceID);
+
+  // Set the field of view parameters for an HMD identified by aDeviceID
+  async SetFOV(uint32_t aDeviceID, VRFieldOfView aFOVLeft,
+               VRFieldOfView aFOVRight, double zNear, double zFar);
+
+child:
+
+  // Notify children of updated VR device enumeration and details.  This will
+  // be sent to all children when the parent receives RefreshDevices, even
+  // if no changes have been detected.  This ensures that Promises exposed
+  // through DOM calls are always resolved.
+  async UpdateDeviceInfo(VRDeviceUpdate[] aDeviceUpdates);
+
+  // Notify children of updated VR device sensor states.  This will be
+  // sent once per frame for at least one second after the parent receives
+  // KeepSensorTracking.
+  async UpdateDeviceSensors(VRSensorUpdate[] aDeviceSensorUpdates);
+
+  async __delete__();
+
+};
+
+} // gfx
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "VRManagerChild.h"
+#include "VRManagerParent.h"
+#include "VRDeviceProxy.h"
+#include "VRDeviceProxyOrientationFallBack.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/layers/CompositorParent.h" // for CompositorParent
+#include "mozilla/dom/Navigator.h"
+
+namespace mozilla {
+namespace gfx {
+
+static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton;
+static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton;
+
+void ReleaseVRManagerParentSingleton() {
+  sVRManagerParentSingleton = nullptr;
+}
+
+VRManagerChild::VRManagerChild()
+{
+  MOZ_COUNT_CTOR(VRManagerChild);
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+VRManagerChild::~VRManagerChild()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_COUNT_DTOR(VRManagerChild);
+
+  Transport* trans = GetTransport();
+  if (trans) {
+    MOZ_ASSERT(XRE_GetIOMessageLoop());
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new DeleteTask<Transport>(trans));
+  }
+}
+
+/*static*/ VRManagerChild*
+VRManagerChild::Get()
+{
+  MOZ_ASSERT(sVRManagerChildSingleton);
+  return sVRManagerChildSingleton;
+}
+
+/*static*/ VRManagerChild*
+VRManagerChild::StartUpInChildProcess(Transport* aTransport, ProcessId aOtherPid)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // There's only one VRManager per child process.
+  MOZ_ASSERT(!sVRManagerChildSingleton);
+
+  RefPtr<VRManagerChild> child(new VRManagerChild());
+  if (!child->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
+    NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
+    return nullptr;
+  }
+
+  sVRManagerChildSingleton = child;
+
+  return sVRManagerChildSingleton;
+}
+
+/*static*/ void
+VRManagerChild::StartUpSameProcess()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
+  if (sVRManagerChildSingleton == nullptr) {
+    sVRManagerChildSingleton = new VRManagerChild();
+    sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
+    sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(),
+                                   mozilla::layers::CompositorParent::CompositorLoop(),
+                                   mozilla::ipc::ChildSide);
+  }
+}
+
+/*static*/ void
+VRManagerChild::ShutDown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (sVRManagerChildSingleton) {
+    sVRManagerChildSingleton->Destroy();
+    sVRManagerChildSingleton = nullptr;
+  }
+}
+
+/*static*/ void
+VRManagerChild::DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild)
+{
+    aVRManagerChild->Close();
+}
+
+void
+VRManagerChild::Destroy()
+{
+  // This must not be called from the destructor!
+  MOZ_ASSERT(mRefCnt != 0);
+
+  // Keep ourselves alive until everything has been shut down
+  RefPtr<VRManagerChild> selfRef = this;
+
+  // The DeferredDestroyVRManager task takes ownership of
+  // the VRManagerChild and will release it when it runs.
+  MessageLoop::current()->PostTask(FROM_HERE,
+             NewRunnableFunction(DeferredDestroy, selfRef));
+}
+
+bool
+VRManagerChild::RecvUpdateDeviceInfo(nsTArray<VRDeviceUpdate>&& aDeviceUpdates)
+{
+  // mDevices could be a hashed container for more scalability, but not worth
+  // it now as we expect < 10 entries.
+  nsTArray<RefPtr<VRDeviceProxy> > devices;
+  for (auto& deviceUpdate: aDeviceUpdates) {
+    bool isNewDevice = true;
+    for (auto& device: mDevices) {
+      if (device->GetDeviceInfo().GetDeviceID() == deviceUpdate.mDeviceInfo.GetDeviceID()) {
+        device->UpdateDeviceInfo(deviceUpdate);
+        devices.AppendElement(device);
+        isNewDevice = false;
+        break;
+      }
+    }
+    if (isNewDevice) {
+      if (deviceUpdate.mDeviceInfo.GetUseMainThreadOrientation()) {
+        devices.AppendElement(new VRDeviceProxyOrientationFallBack(deviceUpdate));
+      } else {
+        devices.AppendElement(new VRDeviceProxy(deviceUpdate));
+      }
+    }
+  }
+
+  mDevices = devices;
+
+
+  for (auto& nav: mNavigatorCallbacks) {
+    nav->NotifyVRDevicesUpdated();
+  }
+  mNavigatorCallbacks.Clear();
+
+  return true;
+}
+
+bool
+VRManagerChild::RecvUpdateDeviceSensors(nsTArray<VRSensorUpdate>&& aDeviceSensorUpdates)
+{
+  // mDevices could be a hashed container for more scalability, but not worth
+  // it now as we expect < 10 entries.
+  for (auto& sensorUpdate: aDeviceSensorUpdates) {
+    for (auto& device: mDevices) {
+      if (device->GetDeviceInfo().GetDeviceID() == sensorUpdate.mDeviceID) {
+        device->UpdateSensorState(sensorUpdate.mSensorState);
+        break;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool
+VRManagerChild::GetVRDevices(nsTArray<RefPtr<VRDeviceProxy> >& aDevices)
+{
+  aDevices = mDevices;
+  return true;
+}
+
+bool
+VRManagerChild::RefreshVRDevicesWithCallback(dom::Navigator* aNavigator)
+{
+  bool success = SendRefreshDevices();
+  if (success) {
+    mNavigatorCallbacks.AppendElement(aNavigator);
+  }
+  return success;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_VR_VRMANAGERCHILD_H
+#define MOZILLA_GFX_VR_VRMANAGERCHILD_H
+
+#include "mozilla/gfx/PVRManagerChild.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+
+namespace mozilla {
+namespace dom {
+class Navigator;
+class VRDevice;
+} // namespace dom
+namespace gfx {
+class VRDeviceProxy;
+
+
+class VRManagerChild : public PVRManagerChild
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRManagerChild)
+
+  bool GetVRDevices(nsTArray<RefPtr<VRDeviceProxy> >& aDevices);
+  bool RefreshVRDevicesWithCallback(dom::Navigator* aNavigator);
+  static VRManagerChild* StartUpInChildProcess(Transport* aTransport,
+                                               ProcessId aOtherProcess);
+
+  static void StartUpSameProcess();
+  static void ShutDown();
+
+
+  static VRManagerChild* Get();
+
+protected:
+  explicit VRManagerChild();
+  ~VRManagerChild();
+  void Destroy();
+  static void DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild);
+
+  virtual bool RecvUpdateDeviceInfo(nsTArray<VRDeviceUpdate>&& aDeviceUpdates) override;
+  virtual bool RecvUpdateDeviceSensors(nsTArray<VRSensorUpdate>&& aDeviceSensorUpdates) override;
+
+  friend class layers::CompositorChild;
+
+private:
+
+  nsTArray<RefPtr<VRDeviceProxy> > mDevices;
+  nsTArray<dom::Navigator*> mNavigatorCallbacks;
+
+};
+
+} // namespace mozilla
+} // namespace gfx
+
+#endif // MOZILLA_GFX_VR_VRMANAGERCHILD_H
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "VRManagerParent.h"
+#include "mozilla/gfx/PVRManagerParent.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h"       // for IToplevelProtocol
+#include "mozilla/TimeStamp.h"               // for TimeStamp
+#include "mozilla/layers/CompositorParent.h"
+#include "mozilla/unused.h"
+#include "VRManager.h"
+
+namespace mozilla {
+namespace layers {
+
+// defined in CompositorParent.cpp
+CompositorThreadHolder* GetCompositorThreadHolder();
+
+} // namespace layers
+
+namespace gfx {
+
+VRManagerParent::VRManagerParent(MessageLoop* aLoop,
+                                 Transport* aTransport,
+                                 ProcessId aChildProcessId)
+{
+  MOZ_COUNT_CTOR(VRManagerParent);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  SetTransport(aTransport);
+  SetOtherProcessId(aChildProcessId);
+}
+
+VRManagerParent::~VRManagerParent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!mVRManagerHolder);
+
+  Transport* trans = GetTransport();
+  if (trans) {
+    MOZ_ASSERT(XRE_GetIOMessageLoop());
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
+                                     new DeleteTask<Transport>(trans));
+  }
+  MOZ_COUNT_DTOR(VRManagerParent);
+}
+
+void VRManagerParent::RegisterWithManager()
+{
+  VRManager* vm = VRManager::Get();
+  vm->AddVRManagerParent(this);
+  mVRManagerHolder = vm;
+}
+
+void VRManagerParent::UnregisterFromManager()
+{
+  VRManager* vm = VRManager::Get();
+  vm->RemoveVRManagerParent(this);
+  mVRManagerHolder = nullptr;
+}
+
+/*static*/ void
+VRManagerParent::ConnectVRManagerInParentProcess(VRManagerParent* aVRManager,
+                                ipc::Transport* aTransport,
+                                base::ProcessId aOtherPid)
+{
+  aVRManager->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
+  aVRManager->RegisterWithManager();
+}
+
+/*static*/ VRManagerParent*
+VRManagerParent::CreateCrossProcess(Transport* aTransport, ProcessId aChildProcessId)
+{
+  MessageLoop* loop = mozilla::layers::CompositorParent::CompositorLoop();
+  RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, aTransport, aChildProcessId);
+  vmp->mSelfRef = vmp;
+  loop->PostTask(FROM_HERE,
+                 NewRunnableFunction(ConnectVRManagerInParentProcess,
+                                     vmp.get(), aTransport, aChildProcessId));
+  return vmp.get();
+}
+
+/*static*/ void
+VRManagerParent::RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager)
+{
+  aVRManager->RegisterWithManager();
+}
+
+/*static*/ VRManagerParent*
+VRManagerParent::CreateSameProcess()
+{
+  MessageLoop* loop = mozilla::layers::CompositorParent::CompositorLoop();
+  RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, nullptr, base::GetCurrentProcId());
+  vmp->mCompositorThreadHolder = layers::GetCompositorThreadHolder();
+  vmp->mSelfRef = vmp;
+  loop->PostTask(FROM_HERE,
+                 NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get()));
+  return vmp.get();
+}
+
+void
+VRManagerParent::DeferredDestroy()
+{
+  MOZ_ASSERT(mCompositorThreadHolder);
+  mCompositorThreadHolder = nullptr;
+  mSelfRef = nullptr;
+}
+
+void
+VRManagerParent::ActorDestroy(ActorDestroyReason why)
+{
+  UnregisterFromManager();
+  MessageLoop::current()->PostTask(
+    FROM_HERE,
+    NewRunnableMethod(this, &VRManagerParent::DeferredDestroy));
+}
+
+mozilla::ipc::IToplevelProtocol*
+VRManagerParent::CloneToplevel(const InfallibleTArray<mozilla::ipc::ProtocolFdMapping>& aFds,
+                               base::ProcessHandle aPeerProcess,
+                               mozilla::ipc::ProtocolCloneContext* aCtx)
+{
+  for (unsigned int i = 0; i < aFds.Length(); i++) {
+    if (aFds[i].protocolId() == unsigned(GetProtocolId())) {
+      Transport* transport = OpenDescriptor(aFds[i].fd(),
+                                            Transport::MODE_SERVER);
+      PVRManagerParent* vm = CreateCrossProcess(transport, base::GetProcId(aPeerProcess));
+      vm->CloneManagees(this, aCtx);
+      vm->IToplevelProtocol::SetTransport(transport);
+      // The reference to the compositor thread is held in OnChannelConnected().
+      // We need to do this for cloned actors, too.
+      vm->OnChannelConnected(base::GetProcId(aPeerProcess));
+      return vm;
+    }
+  }
+  return nullptr;
+}
+
+void
+VRManagerParent::OnChannelConnected(int32_t aPid)
+{
+  mCompositorThreadHolder = layers::GetCompositorThreadHolder();
+}
+
+bool
+VRManagerParent::RecvRefreshDevices()
+{
+  VRManager* vm = VRManager::Get();
+  vm->RefreshVRDevices();
+
+  return true;
+}
+
+bool
+VRManagerParent::RecvResetSensor(const uint32_t& aDeviceID)
+{
+  VRManager* vm = VRManager::Get();
+  RefPtr<gfx::VRHMDInfo> device = vm->GetDevice(aDeviceID);
+  if (device != nullptr) {
+    device->ZeroSensor();
+  }
+
+  return true;
+}
+
+bool
+VRManagerParent::RecvKeepSensorTracking(const uint32_t& aDeviceID)
+{
+  VRManager* vm = VRManager::Get();
+  RefPtr<gfx::VRHMDInfo> device = vm->GetDevice(aDeviceID);
+  if (device != nullptr) {
+    Unused << device->KeepSensorTracking();
+  }
+  return true;
+}
+
+bool
+VRManagerParent::RecvSetFOV(const uint32_t& aDeviceID,
+                            const VRFieldOfView& aFOVLeft,
+                            const VRFieldOfView& aFOVRight,
+                            const double& zNear,
+                            const double& zFar)
+{
+  VRManager* vm = VRManager::Get();
+  RefPtr<gfx::VRHMDInfo> device = vm->GetDevice(aDeviceID);
+  if (device != nullptr) {
+    device->SetFOV(aFOVLeft, aFOVRight, zNear, zFar);
+  }
+  return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_VR_VRMANAGERPARENT_H
+#define MOZILLA_GFX_VR_VRMANAGERPARENT_H
+
+#include "mozilla/layers/CompositorParent.h" // for CompositorThreadHolder
+#include "mozilla/gfx/PVRManagerParent.h" // for PVRManagerParent
+#include "mozilla/ipc/ProtocolUtils.h"    // for IToplevelProtocol
+#include "mozilla/TimeStamp.h"            // for TimeStamp
+#include "gfxVR.h"                        // for VRFieldOfView
+
+namespace mozilla {
+namespace gfx {
+
+class VRManager;
+
+class VRManagerParent final : public PVRManagerParent
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRManagerParent)
+public:
+  VRManagerParent(MessageLoop* aLoop, Transport* aTransport, ProcessId aChildProcessId);
+
+  static VRManagerParent* CreateCrossProcess(Transport* aTransport,
+                                              ProcessId aOtherProcess);
+  static VRManagerParent* CreateSameProcess();
+
+
+  // Overriden from IToplevelProtocol
+  ipc::IToplevelProtocol*
+  CloneToplevel(const InfallibleTArray<ipc::ProtocolFdMapping>& aFds,
+                base::ProcessHandle aPeerProcess,
+                mozilla::ipc::ProtocolCloneContext* aCtx) override;
+
+protected:
+  ~VRManagerParent();
+
+  virtual void ActorDestroy(ActorDestroyReason why) override;
+  void OnChannelConnected(int32_t pid) override;
+
+  virtual bool RecvRefreshDevices() override;
+  virtual bool RecvResetSensor(const uint32_t& aDeviceID) override;
+  virtual bool RecvKeepSensorTracking(const uint32_t& aDeviceID) override;
+  virtual bool RecvSetFOV(const uint32_t& aDeviceID,
+                          const VRFieldOfView& aFOVLeft,
+                          const VRFieldOfView& aFOVRight,
+                          const double& zNear,
+                          const double& zFar) override;
+
+private:
+
+  void RegisterWithManager();
+  void UnregisterFromManager();
+
+  static void RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager);
+  static void ConnectVRManagerInParentProcess(VRManagerParent* aVRManager,
+                                              ipc::Transport* aTransport,
+                                              base::ProcessId aOtherPid);
+
+  void DeferredDestroy();
+
+  // This keeps us alive until ActorDestroy(), at which point we do a
+  // deferred destruction of ourselves.
+  RefPtr<VRManagerParent> mSelfRef;
+
+  // Keep the compositor thread alive, until we have destroyed ourselves.
+  RefPtr<layers::CompositorThreadHolder> mCompositorThreadHolder;
+
+  // Keep the VRManager alive, until we have destroyed ourselves.
+  RefPtr<VRManager> mVRManagerHolder;
+};
+
+} // namespace mozilla
+} // namespace gfx
+
+#endif // MOZILLA_GFX_VR_VRMANAGERPARENT_H
new file mode 100644
--- /dev/null
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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 mozilla_gfx_vr_VRMessageUtils_h
+#define mozilla_gfx_vr_VRMessageUtils_h
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "VRManager.h"
+
+#include "gfxVR.h"
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::gfx::VRHMDType> :
+  public ContiguousEnumSerializer<mozilla::gfx::VRHMDType,
+                                  mozilla::gfx::VRHMDType(0),
+                                  mozilla::gfx::VRHMDType(mozilla::gfx::VRHMDType::NumHMDTypes)> {};
+
+template<>
+struct ParamTraits<mozilla::gfx::VRStateValidFlags> :
+  public BitFlagsEnumSerializer<mozilla::gfx::VRStateValidFlags,
+                                mozilla::gfx::VRStateValidFlags::State_All> {};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRDeviceUpdate>
+{
+  typedef mozilla::gfx::VRDeviceUpdate paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mDeviceInfo);
+    WriteParam(aMsg, aParam.mSensorState);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mDeviceInfo)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mSensorState))) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRSensorUpdate>
+{
+  typedef mozilla::gfx::VRSensorUpdate paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mDeviceID);
+    WriteParam(aMsg, aParam.mSensorState);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mDeviceID)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mSensorState))) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRDeviceInfo>
+{
+  typedef mozilla::gfx::VRDeviceInfo paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mType);
+    WriteParam(aMsg, aParam.mDeviceID);
+    WriteParam(aMsg, aParam.mDeviceName);
+    WriteParam(aMsg, aParam.mSupportedSensorBits);
+    WriteParam(aMsg, aParam.mEyeResolution);
+    WriteParam(aMsg, aParam.mScreenRect);
+    WriteParam(aMsg, aParam.mIsFakeScreen);
+    WriteParam(aMsg, aParam.mUseMainThreadOrientation);
+    for (int i = 0; i < mozilla::gfx::VRDeviceInfo::NumEyes; i++) {
+      WriteParam(aMsg, aParam.mMaximumEyeFOV[i]);
+      WriteParam(aMsg, aParam.mRecommendedEyeFOV[i]);
+      WriteParam(aMsg, aParam.mEyeFOV[i]);
+      WriteParam(aMsg, aParam.mEyeTranslation[i]);
+      WriteParam(aMsg, aParam.mEyeProjectionMatrix[i]);
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mType)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mDeviceID)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mDeviceName)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mSupportedSensorBits)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mEyeResolution)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mScreenRect)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mIsFakeScreen)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mUseMainThreadOrientation))
+        ) {
+      return false;
+    }
+    for (int i = 0; i < mozilla::gfx::VRDeviceInfo::NumEyes; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mMaximumEyeFOV[i])) ||
+          !ReadParam(aMsg, aIter, &(aResult->mRecommendedEyeFOV[i])) ||
+          !ReadParam(aMsg, aIter, &(aResult->mEyeFOV[i])) ||
+          !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i])) ||
+          !ReadParam(aMsg, aIter, &(aResult->mEyeProjectionMatrix[i]))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRHMDSensorState>
+{
+  typedef mozilla::gfx::VRHMDSensorState paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.timestamp);
+    WriteParam(aMsg, aParam.flags);
+    WriteParam(aMsg, aParam.orientation[0]);
+    WriteParam(aMsg, aParam.orientation[1]);
+    WriteParam(aMsg, aParam.orientation[2]);
+    WriteParam(aMsg, aParam.orientation[3]);
+    WriteParam(aMsg, aParam.position[0]);
+    WriteParam(aMsg, aParam.position[1]);
+    WriteParam(aMsg, aParam.position[2]);
+    WriteParam(aMsg, aParam.angularVelocity[0]);
+    WriteParam(aMsg, aParam.angularVelocity[1]);
+    WriteParam(aMsg, aParam.angularVelocity[2]);
+    WriteParam(aMsg, aParam.angularAcceleration[0]);
+    WriteParam(aMsg, aParam.angularAcceleration[1]);
+    WriteParam(aMsg, aParam.angularAcceleration[2]);
+    WriteParam(aMsg, aParam.linearVelocity[0]);
+    WriteParam(aMsg, aParam.linearVelocity[1]);
+    WriteParam(aMsg, aParam.linearVelocity[2]);
+    WriteParam(aMsg, aParam.linearAcceleration[0]);
+    WriteParam(aMsg, aParam.linearAcceleration[1]);
+    WriteParam(aMsg, aParam.linearAcceleration[2]);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->timestamp)) ||
+        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
+        !ReadParam(aMsg, aIter, &(aResult->orientation[0])) ||
+        !ReadParam(aMsg, aIter, &(aResult->orientation[1])) ||
+        !ReadParam(aMsg, aIter, &(aResult->orientation[2])) ||
+        !ReadParam(aMsg, aIter, &(aResult->orientation[3])) ||
+        !ReadParam(aMsg, aIter, &(aResult->position[0])) ||
+        !ReadParam(aMsg, aIter, &(aResult->position[1])) ||
+        !ReadParam(aMsg, aIter, &(aResult->position[2])) ||
+        !ReadParam(aMsg, aIter, &(aResult->angularVelocity[0])) ||
+        !ReadParam(aMsg, aIter, &(aResult->angularVelocity[1])) ||
+        !ReadParam(aMsg, aIter, &(aResult->angularVelocity[2])) ||
+        !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[0])) ||
+        !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[1])) ||
+        !ReadParam(aMsg, aIter, &(aResult->angularAcceleration[2])) ||
+        !ReadParam(aMsg, aIter, &(aResult->linearVelocity[0])) ||
+        !ReadParam(aMsg, aIter, &(aResult->linearVelocity[1])) ||
+        !ReadParam(aMsg, aIter, &(aResult->linearVelocity[2])) ||
+        !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[0])) ||
+        !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[1])) ||
+        !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2]))) {
+      return false;
+    }
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRFieldOfView>
+{
+  typedef mozilla::gfx::VRFieldOfView paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.upDegrees);
+    WriteParam(aMsg, aParam.rightDegrees);
+    WriteParam(aMsg, aParam.downDegrees);
+    WriteParam(aMsg, aParam.leftDegrees);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->upDegrees)) ||
+        !ReadParam(aMsg, aIter, &(aResult->rightDegrees)) ||
+        !ReadParam(aMsg, aIter, &(aResult->downDegrees)) ||
+        !ReadParam(aMsg, aIter, &(aResult->leftDegrees))) {
+      return false;
+    }
+
+    return true;
+  }
+};
+
+} // namespace IPC
+
+#endif // mozilla_gfx_vr_VRMessageUtils_h
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -1,32 +1,46 @@
 # -*- Mode: python; c-basic-offset: 4; 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 += [
     'gfxVR.h',
+    'ipc/VRManagerChild.h',
+    'ipc/VRManagerParent.h',
+    'ipc/VRMessageUtils.h',
+    'VRDeviceProxy.h',
+    'VRManager.h',
 ]
 
 LOCAL_INCLUDES += [
     '/gfx/thebes',
 ]
 
 UNIFIED_SOURCES += [
     'gfxVR.cpp',
     'gfxVRCardboard.cpp',
     'gfxVROculus.cpp',
+    'ipc/VRManagerChild.cpp',
+    'ipc/VRManagerParent.cpp',
+    'VRDeviceProxy.cpp',
+    'VRDeviceProxyOrientationFallBack.cpp',
+    'VRManager.cpp',
 ]
 
 SOURCES += [
     'gfxVROculus050.cpp',
 ]
 
+IPDL_SOURCES = [
+    'ipc/PVRManager.ipdl',
+]
+
 # For building with the real SDK instead of our local hack
 #SOURCES += [
 #    'OVR_CAPI_Util.cpp',
 #    'OVR_CAPIShim.c',
 #    'OVR_StereoProjection.cpp',
 #]
 #
 #CXXFLAGS += ["-Ic:/proj/ovr/OculusSDK-0.6.0-beta/LibOVR/Include"]
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -6202,17 +6202,17 @@ nsDisplaySVGEffects::nsDisplaySVGEffects
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplaySVGEffects::~nsDisplaySVGEffects()
 {
   MOZ_COUNT_DTOR(nsDisplaySVGEffects);
 }
 #endif
 
 nsDisplayVR::nsDisplayVR(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-                         nsDisplayList* aList, mozilla::gfx::VRHMDInfo* aHMD)
+                         nsDisplayList* aList, mozilla::gfx::VRDeviceProxy* aHMD)
   : nsDisplayOwnLayer(aBuilder, aFrame, aList)
   , mHMD(aHMD)
 {
 }
 
 already_AddRefed<Layer>
 nsDisplayVR::BuildLayer(nsDisplayListBuilder* aBuilder,
                         LayerManager* aManager,
@@ -6220,17 +6220,17 @@ nsDisplayVR::BuildLayer(nsDisplayListBui
 {
   ContainerLayerParameters newContainerParameters = aContainerParameters;
   uint32_t flags = FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS |
                    FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
   RefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
                            newContainerParameters, nullptr, flags);
 
-  container->SetVRHMDInfo(mHMD);
+  container->SetVRDeviceID(mHMD->GetDeviceInfo().GetDeviceID());
   container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
                          /*the value is irrelevant*/nullptr);
 
   return container.forget();
 }
 nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                               bool* aSnap)
 {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -49,17 +49,17 @@ namespace mozilla {
 class FrameLayerBuilder;
 class DisplayItemScrollClip;
 namespace layers {
 class Layer;
 class ImageLayer;
 class ImageContainer;
 } // namespace layers
 namespace gfx {
-class VRHMDInfo;
+class VRDeviceProxy;
 } // namespace gfx
 } // namespace mozilla
 
 // A set of blend modes, that never includes OP_OVER (since it's
 // considered the default, rather than a specific blend mode).
 typedef mozilla::EnumSet<mozilla::gfx::CompositionOp> BlendModeSet;
 
 /*
@@ -4292,26 +4292,26 @@ public:
 /**
  * A wrapper layer that wraps its children in a container, then renders
  * everything with an appropriate VR effect based on the HMDInfo.
  */
 
 class nsDisplayVR : public nsDisplayOwnLayer {
 public:
   nsDisplayVR(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-              nsDisplayList* aList, mozilla::gfx::VRHMDInfo* aHMD);
+              nsDisplayList* aList, mozilla::gfx::VRDeviceProxy* aHMD);
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) override
   {
     return mozilla::LAYER_ACTIVE;
   }
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
 
 protected:
-  RefPtr<mozilla::gfx::VRHMDInfo> mHMD;
+  RefPtr<mozilla::gfx::VRDeviceProxy> mHMD;
 };
 
 #endif /*NSDISPLAYLIST_H_*/
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -107,17 +107,17 @@ using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
 
 namespace mozilla {
 namespace gfx {
-class VRHMDInfo;
+class VRDeviceProxy;
 } // namespace gfx
 } // namespace mozilla
 
 // Struct containing cached metrics for box-wrapped frames.
 struct nsBoxLayoutMetrics
 {
   nsSize mPrefSize;
   nsSize mMinSize;
@@ -2042,19 +2042,19 @@ nsIFrame::BuildDisplayListForStackingCon
     IsScrollFrameActive(aBuilder,
                         nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
                         nsLayoutUtils::SCROLLABLE_SAME_DOC |
                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingDisplayList(aBuilder, this, dirtyRect, true);
 
-  mozilla::gfx::VRHMDInfo* vrHMDInfo = nullptr;
+  mozilla::gfx::VRDeviceProxy* vrHMDInfo = nullptr;
   if ((GetStateBits() & NS_FRAME_HAS_VR_CONTENT)) {
-    vrHMDInfo = static_cast<mozilla::gfx::VRHMDInfo*>(mContent->GetProperty(nsGkAtoms::vr_state));
+    vrHMDInfo = static_cast<mozilla::gfx::VRDeviceProxy*>(mContent->GetProperty(nsGkAtoms::vr_state));
   }
 
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
   nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex perspectiveIndex(aBuilder, this);
 
   if (isTransformed || useBlendMode || usingSVGEffects || useStickyPosition) {
     // We don't need to pass ancestor clipping down to our children;