merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 30 Dec 2015 12:00:03 +0100
changeset 312997 c690c50b2b543b420803e8192d6e08e06b20e0a3
parent 312915 115a945b9f8d7ae38ec089a1f20a4cb73adab4b0 (current diff)
parent 312996 46da19b556726f08ff6e9e38b007f5f42a4f6aeb (diff)
child 313000 626d2a7e3a4af515f9c0b611c59fd5128f08328c
child 313014 718d15422bb35afd886fc4350e487bef967fcf7e
child 313032 b493cf33851fc44f9fef8179bc88969f10df3ff6
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)
reviewersmerge
milestone46.0a1
first release with
nightly linux32
c690c50b2b54 / 46.0a1 / 20151230030234 / files
nightly linux64
c690c50b2b54 / 46.0a1 / 20151230030234 / files
nightly mac
c690c50b2b54 / 46.0a1 / 20151230030234 / files
nightly win32
c690c50b2b54 / 46.0a1 / 20151230030234 / files
nightly win64
c690c50b2b54 / 46.0a1 / 20151230030234 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/tests/mochitest/general/test_bug1077002.html
gfx/tests/crashtests/358732-1.xhtml
gfx/tests/crashtests/358732-2.svg
gfx/tests/crashtests/358732-3.html
gfx/tests/crashtests/358732-iframe.html
js/src/asmjs/AsmJSFrameIterator.cpp
js/src/asmjs/AsmJSFrameIterator.h
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSLink.h
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSSignalHandlers.cpp
js/src/asmjs/AsmJSSignalHandlers.h
js/src/asmjs/AsmJSValidate.cpp
js/src/asmjs/AsmJSValidate.h
js/src/asmjs/Wasm.h
js/src/asmjs/WasmCompileArgs.h
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5532,42 +5532,50 @@
         let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
         let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
         let canvas = this._dndCanvas ? this._dndCanvas
                                      : document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
         canvas.mozOpaque = true;
         canvas.width = 160 * scale;
         canvas.height = 90 * scale;
         let toDrag;
+        let dragImageOffset = -16;
         if (gMultiProcessBrowser) {
           var context = canvas.getContext('2d');
           context.fillStyle = "white";
           context.fillRect(0, 0, canvas.width, canvas.height);
           // Create a panel to use it in setDragImage
           // which will tell xul to render a panel that follows
           // the pointer while a dnd session is on.
           if (!this._dndPanel) {
             this._dndCanvas = canvas;
             this._dndPanel = document.createElement("panel");
             this._dndPanel.setAttribute("type", "drag");
-            this._dndPanel.appendChild(canvas);
+            let wrapper = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+            wrapper.style.width = "160px";
+            wrapper.style.height = "90px";
+            wrapper.appendChild(canvas);
+            canvas.style.width = "100%";
+            canvas.style.height = "100%";
+            this._dndPanel.appendChild(wrapper);
             document.documentElement.appendChild(this._dndPanel);
           }
           // PageThumb is async with e10s but that's fine
           // since we can update the panel during the dnd.
           PageThumbs.captureToCanvas(browser, canvas);
           toDrag = this._dndPanel;
         } else {
           // For the non e10s case we can just use PageThumbs
           // sync. No need for xul magic, the native dnd will
           // be fine, so let's use the canvas for setDragImage.
           PageThumbs.captureToCanvas(browser, canvas);
           toDrag = canvas;
+          dragImageOffset = dragImageOffset * scale;
         }
-        dt.setDragImage(toDrag, -16 * scale, -16 * scale);
+        dt.setDragImage(toDrag, dragImageOffset, dragImageOffset);
 
         // _dragData.offsetX/Y give the coordinates that the mouse should be
         // positioned relative to the corner of the new window created upon
         // dragend such that the mouse appears to have the same position
         // relative to the corner of the dragged tab.
         function clientX(ele) {
           return ele.getBoundingClientRect().left;
         }
--- a/configure.in
+++ b/configure.in
@@ -529,16 +529,20 @@ case "$target" in
             # -Wv:18 disables all warnings introduced after VS2013
             # See http://blogs.msdn.com/b/vcblog/archive/2014/11/12/improvements-to-warnings-in-the-c-compiler.aspx
             CFLAGS="$CFLAGS -Wv:18"
             CXXFLAGS="$CXXFLAGS -Wv:18"
 
             # -Zc:sizedDealloc- disables C++14 global sized deallocation (see bug 1160146)
             CXXFLAGS="$CXXFLAGS -Zc:sizedDealloc-"
 
+            # Disable C++11 thread-safe statics due to crashes on XP (bug 1204752)
+            # See https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics
+            CXXFLAGS="$CXXFLAGS -Zc:threadSafeInit-"
+
             # https://connect.microsoft.com/VisualStudio/feedback/details/888527/warnings-on-dbghelp-h
             # for dbghelp.h, imagehlp.h, and shobj.h
             # C4091: 'typedef ': ignored on left of '' when no variable is declared
             CFLAGS="$CFLAGS -wd4091"
             CXXFLAGS="$CXXFLAGS -wd4091"
         else
             AC_MSG_ERROR([This version (${_CC_MAJOR_VERSION}.${_CC_MINOR_VERSION}.${_CC_BUILD_VERSION}) of the MSVC compiler is unsupported.
 You must install Visual C++ 2013 Update 3 or newer in order to build.
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -95,20 +95,22 @@ static RedirEntry kRedirMap[] = {
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD
   },
   {
     "serviceworkers", "chrome://global/content/aboutServiceWorkers.xhtml",
     nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT
   },
+#ifndef ANDROID
   {
     "profiles", "chrome://global/content/aboutProfiles.xhtml",
     nsIAboutModule::ALLOW_SCRIPT
   },
+#endif
   // about:srcdoc is unresolvable by specification.  It is included here
   // because the security manager would disallow srcdoc iframes otherwise.
   {
     "srcdoc", "about:blank",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT |
       nsIAboutModule::URI_CAN_LOAD_IN_CHILD
   },
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -175,17 +175,19 @@ const mozilla::Module::ContractIDEntry k
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #ifdef NIGHTLY_BUILD
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "plugins", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+#ifndef ANDROID
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "profiles", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+#endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
   { NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
   { NS_HANDLERSERVICE_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY },
   { NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
--- 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/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -2117,27 +2117,16 @@ public:
 
       mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY,
                                       EmptyCString());
     }
 
     return true;
   }
 
-  bool Suspend(JSContext* aCx) override
-  {
-    {
-      MutexAutoLock lock(mWebSocketImpl->mMutex);
-      mWebSocketImpl->mWorkerShuttingDown = true;
-    }
-
-    mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
-    return true;
-  }
-
 private:
   WebSocketImpl* mWebSocketImpl;
 };
 
 } // namespace
 
 void
 WebSocketImpl::AddRefObject()
--- 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);
 
@@ -4277,17 +4276,17 @@ nsGlobalWindow::IsShowModalDialogEnabled
   static bool sIsDisabled;
   static const char sShowModalDialogPref[] = "dom.disable_window_showModalDialog";
 
   if (!sAddedPrefCache) {
     Preferences::AddBoolVarCache(&sIsDisabled, sShowModalDialogPref, false);
     sAddedPrefCache = true;
   }
 
-  return !sIsDisabled;
+  return !sIsDisabled && !XRE_IsContentProcess();
 }
 
 nsIDOMOfflineResourceList*
 nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   if (!mApplicationCache) {
@@ -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);
 
@@ -8785,17 +8784,17 @@ nsGlobalWindow::ShowModalDialogOuter(con
                                      const nsAString& aOptions, ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   if (mDoc) {
     mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog);
   }
 
-  if (!IsShowModalDialogEnabled() || XRE_IsContentProcess()) {
+  if (!IsShowModalDialogEnabled()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   Telemetry::Accumulate(Telemetry::DOM_WINDOW_SHOWMODALDIALOG_USED, true);
 
   RefPtr<DialogValueHolder> argHolder =
     new DialogValueHolder(nsContentUtils::SubjectPrincipal(), aArgument);
@@ -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/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1306,17 +1306,17 @@ public:
         uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
 
         if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
             flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
 
         if (!mPixelStore_PremultiplyAlpha)
             flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
 
-        gfx::DrawTarget* idealDrawTarget = nullptr; // Don't care for now.
+        RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr; // Don't care for now.
         return nsLayoutUtils::SurfaceFromElement(elem, flags, idealDrawTarget);
     }
 
 protected:
     // Returns false if `object` is null or not valid.
     template<class ObjectType>
     bool ValidateObject(const char* info, ObjectType* object);
 
--- a/dom/events/Touch.cpp
+++ b/dom/events/Touch.cpp
@@ -2,25 +2,47 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Touch.h"
 
 #include "mozilla/dom/EventTarget.h"
-#include "mozilla/dom/TouchBinding.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "nsGlobalWindow.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 
 namespace mozilla {
 namespace dom {
 
+// static
+already_AddRefed<Touch>
+Touch::Constructor(const GlobalObject& aGlobal,
+                   const TouchInit& aParam,
+                   ErrorResult& aRv)
+{
+  // Annoyingly many parameters, make sure the ordering is the same as in the
+  // Touch constructor.
+  RefPtr<Touch> touch = new Touch(aParam.mTarget,
+                                  aParam.mIdentifier,
+                                  aParam.mPageX,
+                                  aParam.mPageY,
+                                  aParam.mScreenX,
+                                  aParam.mScreenY,
+                                  aParam.mClientX,
+                                  aParam.mClientY,
+                                  aParam.mRadiusX,
+                                  aParam.mRadiusY,
+                                  aParam.mRotationAngle,
+                                  aParam.mForce);
+  return touch.forget();
+}
+
 Touch::Touch(EventTarget* aTarget,
              int32_t aIdentifier,
              int32_t aPageX,
              int32_t aPageY,
              int32_t aScreenX,
              int32_t aScreenY,
              int32_t aClientX,
              int32_t aClientY,
--- a/dom/events/Touch.h
+++ b/dom/events/Touch.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Touch_h_
 #define mozilla_dom_Touch_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/dom/TouchBinding.h"
 #include "nsWrapperCache.h"
 #include "nsAutoPtr.h"
 #include "Units.h"
 
 class nsPresContext;
 
 namespace mozilla {
 namespace dom {
@@ -23,16 +24,20 @@ class EventTarget;
 
 class Touch final : public nsISupports
                   , public nsWrapperCache
                   , public WidgetPointerHelper
 {
 public:
   static bool PrefEnabled(JSContext* aCx, JSObject* aGlobal);
 
+  static already_AddRefed<Touch> Constructor(const GlobalObject& aGlobal,
+                                             const TouchInit& aParam,
+                                             ErrorResult& aRv);
+
   Touch(EventTarget* aTarget,
         int32_t aIdentifier,
         int32_t aPageX,
         int32_t aPageY,
         int32_t aScreenX,
         int32_t aScreenY,
         int32_t aClientX,
         int32_t aClientY,
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -202,16 +202,49 @@ TouchEvent::PrefEnabled(JSContext* aCx, 
     }
   }
   if (prefValue) {
     nsContentUtils::InitializeTouchEventTable();
   }
   return prefValue;
 }
 
+// static
+already_AddRefed<Event>
+TouchEvent::Constructor(const GlobalObject& aGlobal,
+                        const nsAString& aType,
+                        const TouchEventInit& aParam,
+                        ErrorResult& aRv)
+{
+  nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
+  RefPtr<TouchEvent> e = new TouchEvent(t, nullptr, nullptr);
+  bool trusted = e->Init(t);
+  RefPtr<TouchList> touches = e->CopyTouches(aParam.mTouches);
+  RefPtr<TouchList> targetTouches = e->CopyTouches(aParam.mTargetTouches);
+  RefPtr<TouchList> changedTouches = e->CopyTouches(aParam.mChangedTouches);
+  e->InitTouchEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
+                    aParam.mDetail, aParam.mCtrlKey, aParam.mAltKey,
+                    aParam.mShiftKey, aParam.mMetaKey, touches, targetTouches,
+                    changedTouches);
+  e->SetTrusted(trusted);
+  return e.forget();
+}
+
+
+already_AddRefed<TouchList>
+TouchEvent::CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches)
+{
+  RefPtr<TouchList> list = new TouchList(GetParentObject());
+  size_t len = aTouches.Length();
+  for (size_t i = 0; i < len; ++i) {
+    list->Append(aTouches[i]);
+  }
+  return list.forget();
+}
+
 bool
 TouchEvent::AltKey()
 {
   return mEvent->AsTouchEvent()->IsAlt();
 }
 
 bool
 TouchEvent::MetaKey()
--- a/dom/events/TouchEvent.h
+++ b/dom/events/TouchEvent.h
@@ -90,16 +90,19 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TouchEvent, UIEvent)
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return TouchEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
+  already_AddRefed<TouchList>
+  CopyTouches(const Sequence<OwningNonNull<Touch>>& aTouches);
+
   TouchList* Touches();
   TouchList* TargetTouches();
   TouchList* ChangedTouches();
 
   bool AltKey();
   bool MetaKey();
   bool CtrlKey();
   bool ShiftKey();
@@ -115,16 +118,21 @@ public:
                       bool aMetaKey,
                       TouchList* aTouches,
                       TouchList* aTargetTouches,
                       TouchList* aChangedTouches);
 
   static bool PrefEnabled(JSContext* aCx = nullptr,
                           JSObject* aGlobal = nullptr);
 
+  static already_AddRefed<Event> Constructor(const GlobalObject& aGlobal,
+                                             const nsAString& aType,
+                                             const TouchEventInit& aParam,
+                                             ErrorResult& aRv);
+
 protected:
   ~TouchEvent() {}
 
   RefPtr<TouchList> mTouches;
   RefPtr<TouchList> mTargetTouches;
   RefPtr<TouchList> mChangedTouches;
 };
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3168,27 +3168,17 @@ void HTMLMediaElement::UpdateSrcMediaStr
     stream->AddListener(mMediaStreamSizeListener);
 
     mWatchManager.Watch(*mMediaStreamListener,
         &HTMLMediaElement::UpdateReadyStateInternal);
 
     stream->AddAudioOutput(this);
     SetVolumeInternal();
 
-  #ifdef MOZ_WIDGET_GONK
-    bool bUseOverlayImage = mSrcStream->AsDOMHwMediaStream() != nullptr;
-  #else
-    bool bUseOverlayImage = false;
-  #endif
-    VideoFrameContainer* container;
-    if (bUseOverlayImage) {
-      container = GetOverlayImageVideoFrameContainer();
-    } else {
-      container = GetVideoFrameContainer();
-    }
+    VideoFrameContainer* container = GetVideoFrameContainer();
     if (container) {
       stream->AddVideoOutput(container);
     }
   } else {
     if (stream) {
       mSrcStreamPausedCurrentTime = CurrentTime();
 
       stream->RemoveListener(mMediaStreamListener);
@@ -4025,32 +4015,16 @@ VideoFrameContainer* HTMLMediaElement::G
   }
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS));
 
   return mVideoFrameContainer;
 }
 
-VideoFrameContainer* HTMLMediaElement::GetOverlayImageVideoFrameContainer()
-{
-  if (mVideoFrameContainer)
-    return mVideoFrameContainer;
-
-  // Only video frames need an image container.
-  if (!IsVideo()) {
-    return nullptr;
-  }
-
-  mVideoFrameContainer =
-    new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY));
-
-  return mVideoFrameContainer;
-}
-
 nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName)
 {
   LOG_EVENT(LogLevel::Debug, ("%p Dispatching event %s", this,
                           NS_ConvertUTF16toUTF8(aName).get()));
 
   // Save events that occur while in the bfcache. These will be dispatched
   // if the page comes out of the bfcache.
   if (mEventDeliveryPaused) {
@@ -4377,16 +4351,19 @@ void HTMLMediaElement::NotifyAddedSource
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
   {
     QueueSelectResourceTask();
   }
 
   // A load was paused in the resource selection algorithm, waiting for
   // a new source child to be added, resume the resource selection algorithm.
   if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
+    // Rest the flag so we don't queue multiple LoadFromSourceTask() when
+    // multiple <source> are attached in an event loop.
+    mLoadWaitStatus = NOT_WAITING;
     QueueLoadFromSourceTask();
   }
 }
 
 nsIContent* HTMLMediaElement::GetNextSource()
 {
   nsCOMPtr<nsIDOMNode> thisDomNode = do_QueryObject(this);
 
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -213,19 +213,16 @@ public:
   // Called by the media decoder to indicate whether the media cache has
   // suspended the channel.
   virtual void NotifySuspendedByCache(bool aIsSuspended) final override;
 
   virtual bool IsActive() const final override;
 
   virtual bool IsHidden() const final override;
 
-  // In order to create overlayImageContainer to support DOMHwMediaStream.
-  VideoFrameContainer* GetOverlayImageVideoFrameContainer();
-
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   B2G_ACL_EXPORT virtual VideoFrameContainer* GetVideoFrameContainer() final override;
   layers::ImageContainer* GetImageContainer();
 
   // Dispatch events
   virtual nsresult DispatchAsyncEvent(const nsAString& aName) final override;
 
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -597,8 +597,9 @@ skip-if = buildapp == 'b2g' # bug 112901
 [test_img_complete.html]
 [test_viewport_resize.html]
 [test_extapp.html]
 [test_image_clone_load.html]
 [test_bug1203668.html]
 [test_bug1166138.html]
 [test_filepicker_default_directory.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
+[test_bug1233598.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1233598.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1233598
+-->
+<head>
+  <title>Test for Bug 1233598</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233598">Mozilla Bug 1233598</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1233598 **/
+
+var i;  // must be out here to trigger the leak
+
+function runTest()
+{
+  i = document.createElement("input");
+  i.setAttribute("type", "file");
+  i.getFilesAndDirectories();  // returns a promise
+  ok(true, "Are we leaking?");
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set":[["dom.input.dirpicker", true]]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
--- 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/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -1038,17 +1038,17 @@ DOMAudioNodeMediaStream::CreateTrackUnio
   RefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aNode);
   stream->InitTrackUnionStream(aWindow, aGraph);
   return stream.forget();
 }
 
 DOMHwMediaStream::DOMHwMediaStream()
 {
 #ifdef MOZ_WIDGET_GONK
-  mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
+  mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
   mOverlayImage = mImageContainer->CreateOverlayImage();
   nsAutoTArray<ImageContainer::NonOwningImage,1> images;
   images.AppendElement(ImageContainer::NonOwningImage(mOverlayImage));
   mImageContainer->SetCurrentImages(images);
 #endif
 }
 
 DOMHwMediaStream::~DOMHwMediaStream()
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2567,16 +2567,17 @@ ProcessedMediaStream::AllocateInputPort(
     }
     virtual void RunDuringShutdown()
     {
       Run();
     }
     RefPtr<MediaInputPort> mPort;
   };
 
+  MOZ_ASSERT(aStream->GraphImpl() == GraphImpl());
   MOZ_ASSERT(aTrackID != TRACK_NONE && aTrackID != TRACK_INVALID,
              "Only TRACK_ANY and explicit ID are allowed");
   RefPtr<MediaInputPort> port = new MediaInputPort(aStream, aTrackID, this,
                                                      aInputNumber, aOutputNumber);
   port->SetGraphImpl(GraphImpl());
   GraphImpl()->AppendMessage(new Message(port));
   return port.forget();
 }
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -360,37 +360,33 @@ AudioContext::CreateMediaElementSource(H
     return nullptr;
   }
 
   RefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv,
                                                                    mDestination->Stream()->Graph());
   if (aRv.Failed()) {
     return nullptr;
   }
-  RefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
-    new MediaElementAudioSourceNode(this, stream);
-  return mediaElementAudioSourceNode.forget();
+  return MediaElementAudioSourceNode::Create(this, stream, aRv);
 }
 
 already_AddRefed<MediaStreamAudioSourceNode>
 AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream,
                                       ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   if (CheckClosed(aRv)) {
     return nullptr;
   }
 
-  RefPtr<MediaStreamAudioSourceNode> mediaStreamAudioSourceNode =
-    new MediaStreamAudioSourceNode(this, &aMediaStream);
-  return mediaStreamAudioSourceNode.forget();
+  return MediaStreamAudioSourceNode::Create(this, &aMediaStream, aRv);
 }
 
 already_AddRefed<GainNode>
 AudioContext::CreateGain(ErrorResult& aRv)
 {
   if (CheckClosed(aRv)) {
     return nullptr;
   }
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -56,17 +56,16 @@ public:
   void Suspend();
   void Resume();
 
   void StartRendering(Promise* aPromise);
 
   void OfflineShutdown();
 
   AudioChannel MozAudioChannelType() const;
-  void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   virtual void NotifyMainThreadStreamFinished() override;
   void FireOfflineCompletionEvent();
 
   // An amount that should be added to the MediaStream's current time to
   // get the AudioContext.currentTime.
   StreamTime ExtraCurrentTime();
 
@@ -86,16 +85,17 @@ public:
 
   void InputMuted(bool aInputMuted);
   void ResolvePromise(AudioBuffer* aRenderedBuffer);
 
 protected:
   virtual ~AudioDestinationNode();
 
 private:
+  void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
   bool CheckAudioChannelPermissions(AudioChannel aValue);
 
   void SetCanPlay(float aVolume, bool aMuted);
 
   void NotifyStableState();
   void ScheduleStableStateNotification();
 
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -49,34 +49,29 @@ AudioNode::AudioNode(AudioContext* aCont
                      ChannelInterpretation aChannelInterpretation)
   : DOMEventTargetHelper(aContext->GetParentObject())
   , mContext(aContext)
   , mChannelCount(aChannelCount)
   , mChannelCountMode(aChannelCountMode)
   , mChannelInterpretation(aChannelInterpretation)
   , mId(gId++)
   , mPassThrough(false)
-#ifdef DEBUG
-  , mDemiseNotified(false)
-#endif
 {
   MOZ_ASSERT(aContext);
   DOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
   aContext->RegisterNode(this);
 }
 
 AudioNode::~AudioNode()
 {
   MOZ_ASSERT(mInputNodes.IsEmpty());
   MOZ_ASSERT(mOutputNodes.IsEmpty());
   MOZ_ASSERT(mOutputParams.IsEmpty());
-#ifdef DEBUG
-  MOZ_ASSERT(mDemiseNotified,
+  MOZ_ASSERT(!mStream,
              "The webaudio-node-demise notification must have been sent");
-#endif
   if (mContext) {
     mContext->UnregisterNode(this);
   }
 }
 
 size_t
 AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
@@ -385,19 +380,16 @@ AudioNode::DestroyMediaStream()
     mStream = nullptr;
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       nsAutoString id;
       id.AppendPrintf("%u", mId);
       obs->NotifyObservers(nullptr, "webaudio-node-demise", id.get());
     }
-#ifdef DEBUG
-    mDemiseNotified = true;
-#endif
   }
 }
 
 void
 AudioNode::RemoveOutputParam(AudioParam* aParam)
 {
   mOutputParams.RemoveElement(aParam);
 }
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -251,19 +251,14 @@ private:
   nsTArray<RefPtr<AudioParam> > mOutputParams;
   uint32_t mChannelCount;
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
   const uint32_t mId;
   // Whether the node just passes through its input.  This is a devtools API that
   // only works for some node types.
   bool mPassThrough;
-#ifdef DEBUG
-  // In debug builds, check to make sure that the node demise notification has
-  // been properly sent before the node is destroyed.
-  bool mDemiseNotified;
-#endif
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/MediaElementAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.cpp
@@ -5,20 +5,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaElementAudioSourceNode.h"
 #include "mozilla/dom/MediaElementAudioSourceNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext,
-                                                         DOMMediaStream* aStream)
-  : MediaStreamAudioSourceNode(aContext, aStream)
+MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext)
+  : MediaStreamAudioSourceNode(aContext)
+{
+}
+
+/* static */ already_AddRefed<MediaElementAudioSourceNode>
+MediaElementAudioSourceNode::Create(AudioContext* aContext,
+                                    DOMMediaStream* aStream, ErrorResult& aRv)
 {
+  RefPtr<MediaElementAudioSourceNode> node =
+    new MediaElementAudioSourceNode(aContext);
+
+  node->Init(aStream, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return node.forget();
 }
 
 JSObject*
 MediaElementAudioSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MediaElementAudioSourceNodeBinding::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/media/webaudio/MediaElementAudioSourceNode.h
+++ b/dom/media/webaudio/MediaElementAudioSourceNode.h
@@ -10,28 +10,30 @@
 #include "MediaStreamAudioSourceNode.h"
 
 namespace mozilla {
 namespace dom {
 
 class MediaElementAudioSourceNode final : public MediaStreamAudioSourceNode
 {
 public:
-  MediaElementAudioSourceNode(AudioContext* aContext,
-                              DOMMediaStream* aStream);
+  static already_AddRefed<MediaElementAudioSourceNode>
+  Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual const char* NodeType() const override
   {
     return "MediaElementAudioSourceNode";
   }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
+private:
+  explicit MediaElementAudioSourceNode(AudioContext* aContext);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -26,28 +26,55 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
-MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext,
-                                                       DOMMediaStream* aMediaStream)
+MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
-              ChannelInterpretation::Speakers),
-    mInputStream(aMediaStream)
+              ChannelInterpretation::Speakers)
+{
+}
+
+/* static */ already_AddRefed<MediaStreamAudioSourceNode>
+MediaStreamAudioSourceNode::Create(AudioContext* aContext,
+                                   DOMMediaStream* aStream, ErrorResult& aRv)
 {
+  RefPtr<MediaStreamAudioSourceNode> node =
+    new MediaStreamAudioSourceNode(aContext);
+
+  node->Init(aStream, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  return node.forget();
+}
+
+void
+MediaStreamAudioSourceNode::Init(DOMMediaStream* aMediaStream, ErrorResult& aRv)
+{
+  MOZ_ASSERT(aMediaStream);
+  MediaStream* inputStream = aMediaStream->GetPlaybackStream();
+  MediaStreamGraph* graph = Context()->Graph();
+  if (NS_WARN_IF(graph != inputStream->Graph())) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  mInputStream = aMediaStream;
   AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this);
-  mStream = AudioNodeExternalInputStream::Create(aContext->Graph(), engine);
+  mStream = AudioNodeExternalInputStream::Create(graph, engine);
   ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get());
-  mInputPort = outputStream->AllocateInputPort(aMediaStream->GetPlaybackStream());
+  mInputPort = outputStream->AllocateInputPort(inputStream);
   mInputStream->AddConsumerToKeepAlive(static_cast<nsIDOMEventTarget*>(this));
 
   PrincipalChanged(mInputStream); // trigger enabling/disabling of the connector
   mInputStream->AddPrincipalChangeObserver(this);
 }
 
 MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
 {
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h
@@ -39,18 +39,18 @@ public:
 private:
   bool mEnabled;
 };
 
 class MediaStreamAudioSourceNode : public AudioNode,
                                    public DOMMediaStream::PrincipalChangeObserver
 {
 public:
-  MediaStreamAudioSourceNode(AudioContext* aContext,
-                             DOMMediaStream* aMediaStream);
+  static already_AddRefed<MediaStreamAudioSourceNode>
+  Create(AudioContext* aContext, DOMMediaStream* aStream, ErrorResult& aRv);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual void DestroyMediaStream() override;
 
@@ -62,16 +62,18 @@ public:
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   virtual void PrincipalChanged(DOMMediaStream* aMediaStream) override;
 
 protected:
+  explicit MediaStreamAudioSourceNode(AudioContext* aContext);
+  void Init(DOMMediaStream* aMediaStream, ErrorResult& aRv);
   virtual ~MediaStreamAudioSourceNode();
 
 private:
   RefPtr<MediaInputPort> mInputPort;
   RefPtr<DOMMediaStream> mInputStream;
 };
 
 } // namespace dom
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2421,27 +2421,31 @@ nsPluginHost::FindPluginsInContent(bool 
   nsTArray<PluginTag> plugins;
   uint32_t parentEpoch;
   if (!cp->SendFindPlugins(ChromeEpochForContent(), &rv, &plugins, &parentEpoch) ||
       NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (parentEpoch != ChromeEpochForContent()) {
-    SetChromeEpochForContent(parentEpoch);
     *aPluginsChanged = true;
     if (!aCreatePluginList) {
       return NS_OK;
     }
 
+    // Don't do this if aCreatePluginList is false. Otherwise, when we actually
+    // want to create the list, we'll come back here and do nothing.
+    SetChromeEpochForContent(parentEpoch);
+
     for (size_t i = 0; i < plugins.Length(); i++) {
       PluginTag& tag = plugins[i];
 
       // Don't add the same plugin again.
-      if (PluginWithId(tag.id())) {
+      if (nsPluginTag* existing = PluginWithId(tag.id())) {
+        UpdateInMemoryPluginInfo(existing);
         continue;
       }
 
       nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
                                                tag.name().get(),
                                                tag.description().get(),
                                                tag.filename().get(),
                                                "", // aFullPath
@@ -2694,24 +2698,19 @@ nsPluginHost::FindPluginsForContent(uint
                                       tag->FileName(),
                                       tag->Version(),
                                       tag->mLastModifiedTime,
                                       tag->IsFromExtension()));
   }
   return NS_OK;
 }
 
-// This function is not relevant for fake plugins.
 void
-nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
+nsPluginHost::UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag)
 {
-  MOZ_ASSERT(XRE_IsParentProcess());
-
-  ReadPluginInfo();
-  WritePluginInfo();
   NS_ITERATIVE_UNREF_LIST(RefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(RefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
   if (!aPluginTag) {
     return;
   }
 
   // Update types with category manager
@@ -2732,16 +2731,30 @@ nsPluginHost::UpdatePluginInfo(nsPluginT
   }
 
   nsCOMPtr<nsIObserverService> obsService =
     mozilla::services::GetObserverService();
   if (obsService)
     obsService->NotifyObservers(nullptr, "plugin-info-updated", nullptr);
 }
 
+// This function is not relevant for fake plugins.
+void
+nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  ReadPluginInfo();
+  WritePluginInfo();
+
+  IncrementChromeEpoch();
+
+  UpdateInMemoryPluginInfo(aPluginTag);
+}
+
 /* static */ bool
 nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
 {
   nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
   if (!whitelist.Length()) {
     return true;
   }
   nsDependentCString wrap(aMimeType);
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -358,16 +358,18 @@ private:
   // To be used by the chrome process; returns the current epoch.
   uint32_t ChromeEpoch();
 
   // To be used by the content process to get/set the last observed epoch value
   // from the chrome process.
   uint32_t ChromeEpochForContent();
   void SetChromeEpochForContent(uint32_t aEpoch);
 
+  void UpdateInMemoryPluginInfo(nsPluginTag* aPluginTag);
+
   // On certain platforms, we only want to load certain plugins. This function
   // centralizes loading rules.
   bool ShouldAddPlugin(nsPluginTag* aPluginTag);
 
   RefPtr<nsPluginTag> mPlugins;
   RefPtr<nsPluginTag> mCachedPlugins;
   RefPtr<nsInvalidPluginTag> mInvalidPlugins;
 
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -571,17 +571,18 @@ nsPluginTag::IsClicktoplay()
 NS_IMETHODIMP
 nsPluginTag::GetClicktoplay(bool *aClicktoplay)
 {
   *aClicktoplay = IsClicktoplay();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPluginTag::GetEnabledState(uint32_t *aEnabledState) {
+nsPluginTag::GetEnabledState(uint32_t *aEnabledState)
+{
   int32_t enabledState;
   nsresult rv = Preferences::GetInt(GetStatePrefNameForPlugin(this).get(),
                                     &enabledState);
   if (NS_SUCCEEDED(rv) &&
       enabledState >= nsIPluginTag::STATE_DISABLED &&
       enabledState <= nsIPluginTag::STATE_ENABLED) {
     *aEnabledState = (uint32_t)enabledState;
     return rv;
@@ -596,17 +597,18 @@ nsPluginTag::GetEnabledState(uint32_t *a
     *aEnabledState = (uint32_t)enabledState;
     return NS_OK;
   }
 
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
-nsPluginTag::SetEnabledState(uint32_t aEnabledState) {
+nsPluginTag::SetEnabledState(uint32_t aEnabledState)
+{
   if (aEnabledState >= ePluginState_MaxValue)
     return NS_ERROR_ILLEGAL_VALUE;
   uint32_t oldState = nsIPluginTag::STATE_DISABLED;
   GetEnabledState(&oldState);
   if (oldState != aEnabledState) {
     Preferences::SetInt(GetStatePrefNameForPlugin(this).get(), aEnabledState);
     if (RefPtr<nsPluginHost> host = nsPluginHost::GetInst()) {
       host->UpdatePluginInfo(this);
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -51,18 +51,16 @@ skip-if = ((buildapp == 'mulet' || build
 [test_bug504220.html]
 [test_bug628069_1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug653364.html]
 [test_bug861217.html]
-[test_bug1077002.html]
-run-if = e10s
 [test_clientRects.html]
 [test_clipboard_events.html]
 skip-if = e10s || buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_consoleAPI.html]
 [test_DOMMatrix.html]
 [test_domWindowUtils.html]
 [test_domWindowUtils_scrollXY.html]
 [test_domWindowUtils_scrollbarSize.html]
@@ -96,16 +94,18 @@ skip-if = buildapp == 'b2g' || buildapp 
 [test_resource_timing.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_resource_timing_cross_origin.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_performance_now.html]
 [test_srcset_pref.html]
 [test_showModalDialog.html]
 skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+[test_showModalDialog_e10s.html]
+run-if = e10s
 [test_stylesheetPI.html]
 [test_vibrator.html]
 skip-if = toolkit == 'android' #CRASH_SUTAGENT
 [test_windowProperties.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_windowedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_navigation_timing.html]
rename from dom/tests/mochitest/general/test_bug1077002.html
rename to dom/tests/mochitest/general/test_showModalDialog_e10s.html
--- a/dom/tests/mochitest/general/test_bug1077002.html
+++ b/dom/tests/mochitest/general/test_showModalDialog_e10s.html
@@ -1,36 +1,32 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1077002
 -->
 <head>
-  <title>Test for Bug 1077002</title>
+  <title>Test for showModalDialog unavailability in e10s</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank"
     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1077002">Mozilla Bug 1077002</a>
 <p id="display"></p>
 <div id="content">
   <iframe id="frame" style="height:100px; width:100px; border:0"></iframe>
   <div id="status" style="display: none"></div>
 </div>
 <pre id="test">
 <script type="application/javascript;version=1.7">
 
-/** Test for Bug 1077002 **/
+/** Test for showModalDialog unavailability in e10s **/
 
-try {
-    // NB: This test runs in e10s only. In e10s showModalDialog should only
-    // ever throw NS_ERROR_NOT_AVAILABLE.
-    window.showModalDialog("http://example.org");
-} catch (e) {
-    is(e.name, "NS_ERROR_NOT_AVAILABLE", "throw the correct error message");
-}
+// NB: This test runs in e10s only. In e10s showModalDialog should not
+// exist.
+ok(!window.showModalDialog, "showModalDialog should not exist");
 
 </script>
 </pre>
 </body>
 </html>
--- 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/dom/webidl/Touch.webidl
+++ b/dom/webidl/Touch.webidl
@@ -5,17 +5,33 @@
  *
  * The origin of this IDL file is
  * http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Func="mozilla::dom::Touch::PrefEnabled"]
+dictionary TouchInit {
+  required long identifier;
+  required EventTarget target;
+  long clientX = 0;
+  long clientY = 0;
+  long screenX = 0;
+  long screenY = 0;
+  long pageX = 0;
+  long pageY = 0;
+  float radiusX = 0;
+  float radiusY = 0;
+  float rotationAngle = 0;
+  float force = 0;
+};
+
+[Constructor(TouchInit touchInitDict), 
+ Func="mozilla::dom::Touch::PrefEnabled"]
 interface Touch {
   readonly    attribute long         identifier;
   readonly    attribute EventTarget? target;
   readonly    attribute long         screenX;
   readonly    attribute long         screenY;
   readonly    attribute long         clientX;
   readonly    attribute long         clientY;
   readonly    attribute long         pageX;
--- a/dom/webidl/TouchEvent.webidl
+++ b/dom/webidl/TouchEvent.webidl
@@ -1,15 +1,22 @@
 /* -*- Mode: IDL; tab-width: 2; 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/.
  */
 
-[Func="mozilla::dom::TouchEvent::PrefEnabled"]
+dictionary TouchEventInit : EventModifierInit {
+  sequence<Touch> touches = [];
+  sequence<Touch> targetTouches = [];
+  sequence<Touch> changedTouches = [];
+};
+
+[Constructor(DOMString type, optional TouchEventInit eventInitDict),
+ Func="mozilla::dom::TouchEvent::PrefEnabled"]
 interface TouchEvent : UIEvent {
   readonly attribute TouchList touches;
   readonly attribute TouchList targetTouches;
   readonly attribute TouchList changedTouches;
 
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
   readonly attribute boolean ctrlKey;
--- a/dom/workers/WorkerFeature.h
+++ b/dom/workers/WorkerFeature.h
@@ -69,17 +69,14 @@ enum Status
   Dead
 };
 
 class WorkerFeature
 {
 public:
   virtual ~WorkerFeature() { }
 
-  virtual bool Suspend(JSContext* aCx) { return true; }
-  virtual bool Resume(JSContext* aCx) { return true; }
-
   virtual bool Notify(JSContext* aCx, Status aStatus) = 0;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_workerfeature_h__ */
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -12,16 +12,20 @@
 #include "gfxFailure.h"
 #include "gfxPrefs.h"
 #include "prenv.h"
 #include "GeckoProfiler.h"
 #include "mozilla/gfx/MacIOSurface.h"
 
 #include <OpenGL/OpenGL.h>
 
+// When running inside a VM, creating an accelerated OpenGL context usually
+// fails. Uncomment this line to emulate that behavior.
+// #define EMULATE_VM
+
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::gfx;
 
 class CGLLibrary
 {
 public:
@@ -169,22 +173,33 @@ GLContextCGL::SwapBuffers()
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
+    NSOpenGLPFAAllowOfflineRenderers,
+    0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = {
     NSOpenGLPFAAccelerated,
     NSOpenGLPFAAllowOfflineRenderers,
     0
 };
 
 static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered[] = {
+    NSOpenGLPFAAllowOfflineRenderers,
+    NSOpenGLPFADoubleBuffer,
+    0
+};
+
+static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel[] = {
     NSOpenGLPFAAccelerated,
     NSOpenGLPFAAllowOfflineRenderers,
     NSOpenGLPFADoubleBuffer,
     0
 };
 
 static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = {
     0
@@ -220,27 +235,33 @@ CreateWithFormat(const NSOpenGLPixelForm
                                 shareContext:nullptr];
 
     [format release];
 
     return context;
 }
 
 already_AddRefed<GLContext>
-GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget)
+GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated)
 {
     if (!sCGLLibrary.EnsureInitialized()) {
         return nullptr;
     }
 
+#ifdef EMULATE_VM
+    if (aForceAccelerated) {
+        return nullptr;
+    }
+#endif
+
     const NSOpenGLPixelFormatAttribute* attribs;
     if (sCGLLibrary.UseDoubleBufferedWindows()) {
-        attribs = kAttribs_doubleBuffered;
+        attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered;
     } else {
-        attribs = kAttribs_singleBuffered;
+        attribs = aForceAccelerated ? kAttribs_singleBuffered_accel : kAttribs_singleBuffered;
     }
     NSOpenGLContext* context = CreateWithFormat(attribs);
     if (!context) {
         return nullptr;
     }
 
     // make the context transparent
     GLint opaque = 0;
--- a/gfx/gl/GLContextProviderEAGL.mm
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -206,17 +206,17 @@ CreateEAGLContext(bool aOffscreen, GLCon
         glContext = nullptr;
         return nullptr;
     }
 
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
-GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget)
+GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
 {
     RefPtr<GLContext> glContext = CreateEAGLContext(false, GetGlobalContextEAGL());
     if (!glContext) {
         return nullptr;
     }
 
     if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) {
         return nullptr;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -744,17 +744,17 @@ GLContextProviderEGL::CreateWrappingExis
 
         return glContext.forget();
     }
 
     return nullptr;
 }
 
 already_AddRefed<GLContext>
-GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget)
+GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
         MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
         return nullptr;
     }
 
     bool doubleBuffered = true;
 
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1047,17 +1047,17 @@ GLContextProviderGLX::CreateWrappingExis
 
         return glContext.forget();
     }
 
     return nullptr;
 }
 
 already_AddRefed<GLContext>
-GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget)
+GLContextProviderGLX::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated)
 {
     if (!sGLXLibrary.EnsureInitialized()) {
         return nullptr;
     }
 
     // Currently, we take whatever Visual the window already has, and
     // try to create an fbconfig for that visual.  This isn't
     // necessarily what we want in the long run; an fbconfig may not
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -30,21 +30,22 @@ public:
      * The GetSharedContext() method will return non-null if sharing
      * was successful.
      *
      * Note: a context created for a widget /must not/ hold a strong
      * reference to the widget; otherwise a cycle can be created through
      * a GL layer manager.
      *
      * @param aWidget Widget whose surface to create a context for
+     * @param aForceAccelerated true if only accelerated contexts are allowed
      *
      * @return Context to use for the window
      */
     static already_AddRefed<GLContext>
-    CreateForWindow(nsIWidget* widget);
+    CreateForWindow(nsIWidget* widget, bool aForceAccelerated);
 
     /**
      * Create a context for offscreen rendering.  The target of this
      * context should be treated as opaque -- it might be a FBO, or a
      * pbuffer, or some other construct.  Users of this GLContext
      * should bind framebuffer 0 directly to use this offscreen buffer.
      *
      * The offscreen context returned by this method will always have
--- a/gfx/gl/GLContextProviderNull.cpp
+++ b/gfx/gl/GLContextProviderNull.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GLContextProvider.h"
 
 namespace mozilla {
 namespace gl {
 
 already_AddRefed<GLContext>
-GLContextProviderNull::CreateForWindow(nsIWidget*)
+GLContextProviderNull::CreateForWindow(nsIWidget*, bool aForceAccelerated)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -433,17 +433,17 @@ GetGlobalContextWGL()
 
 already_AddRefed<GLContext>
 GLContextProviderWGL::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
-GLContextProviderWGL::CreateForWindow(nsIWidget *aWidget)
+GLContextProviderWGL::CreateForWindow(nsIWidget *aWidget, bool aForceAccelerated)
 {
     if (!sWGLLib.EnsureInitialized()) {
         return nullptr;
     }
 
     /**
        * We need to make sure we call SetPixelFormat -after- calling
        * EnsureInitialized, otherwise it can load/unload the dll and
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -135,17 +135,16 @@ enum class EffectTypes : uint8_t {
 
 /**
  * How the Compositable should manage textures.
  */
 enum class CompositableType : uint8_t {
   UNKNOWN,
   CONTENT_TILED,   // tiled painted layer
   IMAGE,           // image with single buffering
-  IMAGE_OVERLAY,   // image without buffer
   IMAGE_BRIDGE,    // ImageBridge protocol
   CONTENT_SINGLE,  // painted layer interface, single buffering
   CONTENT_DOUBLE,  // painted layer interface, double buffering
   COUNT
 };
 
 #ifdef XP_WIN
 typedef void* SyncHandle;
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -129,21 +129,16 @@ ImageContainer::ImageContainer(Mode flag
     switch (flag) {
       case SYNCHRONOUS:
         break;
       case ASYNCHRONOUS:
         mIPDLChild = new ImageContainerChild(this);
         mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this).take();
         MOZ_ASSERT(mImageClient);
         break;
-      case ASYNCHRONOUS_OVERLAY:
-        mIPDLChild = new ImageContainerChild(this);
-        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY, this).take();
-        MOZ_ASSERT(mImageClient);
-        break;
       default:
         MOZ_ASSERT(false, "This flag is invalid.");
         break;
     }
   }
 }
 
 ImageContainer::~ImageContainer()
@@ -174,23 +169,18 @@ ImageContainer::CreateSharedRGBImage()
   return new SharedRGBImage(mImageClient);
 }
 
 #ifdef MOZ_WIDGET_GONK
 RefPtr<OverlayImage>
 ImageContainer::CreateOverlayImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) {
-    // If this ImageContainer is async but the image type mismatch, fix it here
-    if (ImageBridgeChild::IsCreated()) {
-      ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
-      mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(
-          CompositableType::IMAGE_OVERLAY, this).take();
-    }
+  if (!mImageClient || !mImageClient->AsImageClientSingle()) {
+    return nullptr;
   }
   return new OverlayImage();
 }
 #endif
 
 void
 ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
 {
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -345,17 +345,17 @@ protected:
  * updates the shared state to point to the new image and the old image
  * is immediately released (not true in Normal or Asynchronous modes).
  */
 class ImageContainer final : public SupportsWeakPtr<ImageContainer> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
 
-  enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01, ASYNCHRONOUS_OVERLAY = 0x02 };
+  enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01 };
 
   explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
 
   typedef uint32_t FrameID;
   typedef uint32_t ProducerID;
 
   RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
 
--- 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/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -102,24 +102,16 @@ protected:
 
     if (mContainer->IsAsync()) {
       mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE;
       return mImageClientTypeContainer;
     }
 
     AutoLockImage autoLock(mContainer);
 
-#ifdef MOZ_WIDGET_GONK
-    if (autoLock.HasImage() &&
-        autoLock.GetImage()->GetFormat() == ImageFormat::OVERLAY_IMAGE) {
-      mImageClientTypeContainer = CompositableType::IMAGE_OVERLAY;
-      return mImageClientTypeContainer;
-    }
-#endif
-
     mImageClientTypeContainer = autoLock.HasImage()
         ? CompositableType::IMAGE : CompositableType::UNKNOWN;
     return mImageClientTypeContainer;
   }
 
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientTypeContainer;
 };
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -50,21 +50,16 @@ ImageClient::CreateImageClient(Composita
     result = new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE);
     break;
   case CompositableType::IMAGE_BRIDGE:
     result = new ImageClientBridge(aForwarder, aFlags);
     break;
   case CompositableType::UNKNOWN:
     result = nullptr;
     break;
-#ifdef MOZ_WIDGET_GONK
-  case CompositableType::IMAGE_OVERLAY:
-    result = new ImageClientOverlay(aForwarder, aFlags);
-    break;
-#endif
   default:
     MOZ_CRASH("GFX: unhandled program type image");
   }
 
   NS_ASSERTION(result, "Failed to create ImageClient");
 
   return result.forget();
 }
@@ -149,16 +144,31 @@ ImageClientSingle::UpdateImage(ImageCont
     return true;
   }
 
   nsTArray<Buffer> newBuffers;
   nsAutoTArray<CompositableForwarder::TimedTextureClient,4> textures;
 
   for (auto& img : images) {
     Image* image = img.mImage;
+
+#ifdef MOZ_WIDGET_GONK
+    if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) {
+      OverlayImage* overlayImage = static_cast<OverlayImage*>(image);
+      uint32_t overlayId = overlayImage->GetOverlayId();
+      gfx::IntSize size = overlayImage->GetSize();
+
+      OverlaySource source;
+      source.handle() = OverlayHandle(overlayId);
+      source.size() = size;
+      GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
+      continue;
+    }
+#endif
+
     RefPtr<TextureClient> texture = image->GetTextureClient(this);
 
     for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
       if (mBuffers[i].mImageSerial == image->GetSerial()) {
         if (texture) {
           MOZ_ASSERT(texture == mBuffers[i].mTextureClient);
         } else {
           texture = mBuffers[i].mTextureClient;
@@ -311,45 +321,10 @@ ImageClientBridge::UpdateImage(ImageCont
   if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
     return true;
   }
   mAsyncContainerID = aContainer->GetAsyncContainerID();
   static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
   return true;
 }
 
-#ifdef MOZ_WIDGET_GONK
-ImageClientOverlay::ImageClientOverlay(CompositableForwarder* aFwd,
-                                       TextureFlags aFlags)
-  : ImageClient(aFwd, aFlags, CompositableType::IMAGE_OVERLAY)
-{
-}
-
-bool
-ImageClientOverlay::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
-{
-  AutoLockImage autoLock(aContainer);
-
-  Image *image = autoLock.GetImage();
-  if (!image) {
-    return false;
-  }
-
-  if (mLastUpdateGenerationCounter == (uint32_t)image->GetSerial()) {
-    return true;
-  }
-  mLastUpdateGenerationCounter = (uint32_t)image->GetSerial();
-
-  if (image->GetFormat() == ImageFormat::OVERLAY_IMAGE) {
-    OverlayImage* overlayImage = static_cast<OverlayImage*>(image);
-    uint32_t overlayId = overlayImage->GetOverlayId();
-    gfx::IntSize size = overlayImage->GetSize();
-
-    OverlaySource source;
-    source.handle() = OverlayHandle(overlayId);
-    source.size() = size;
-    GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
-  }
-  return true;
-}
-#endif
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -135,34 +135,12 @@ public:
   {
     MOZ_ASSERT(!aChild, "ImageClientBridge should not have IPDL actor");
   }
 
 protected:
   uint64_t mAsyncContainerID;
 };
 
-#ifdef MOZ_WIDGET_GONK
-/**
- * And ImageClient to handle opaque video stream.
- * Such video stream does not upload new Image for each frame.
- * Gecko have no way to get the buffer content from the Image, since the Image
- * does not contain the real buffer.
- * It need special hardware to display the Image
- */
-class ImageClientOverlay : public ImageClient
-{
-public:
-  ImageClientOverlay(CompositableForwarder* aFwd,
-                     TextureFlags aFlags);
-
-  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags);
-  TextureInfo GetTextureInfo() const override
-  {
-    return TextureInfo(CompositableType::IMAGE_OVERLAY);
-  }
-};
-#endif
-
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -197,21 +197,16 @@ CompositableHost::Create(const TextureIn
     NS_ERROR("Cannot create an image bridge compositable this way");
     break;
   case CompositableType::CONTENT_TILED:
     result = new TiledContentHost(aTextureInfo);
     break;
   case CompositableType::IMAGE:
     result = new ImageHost(aTextureInfo);
     break;
-#ifdef MOZ_WIDGET_GONK
-  case CompositableType::IMAGE_OVERLAY:
-    result = new ImageHostOverlay(aTextureInfo);
-    break;
-#endif
   case CompositableType::CONTENT_SINGLE:
     result = new ContentHostSingleBuffered(aTextureInfo);
     break;
   case CompositableType::CONTENT_DOUBLE:
     result = new ContentHostDoubleBuffered(aTextureInfo);
     break;
   default:
     NS_ERROR("Unknown CompositableType");
--- 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/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -127,16 +127,31 @@ ImageHost::RemoveTextureHost(TextureHost
   for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
     if (mImages[i].mFrontBuffer == aTexture) {
       aTexture->UnbindTextureSource();
       mImages.RemoveElementAt(i);
     }
   }
 }
 
+void
+ImageHost::UseOverlaySource(OverlaySource aOverlay,
+                            const gfx::IntRect& aPictureRect)
+{
+  if ((aOverlay.handle().type() == OverlayHandle::Tint32_t) &&
+      aOverlay.handle().get_int32_t() != INVALID_OVERLAY) {
+    if (!mImageHostOverlay) {
+      mImageHostOverlay = new ImageHostOverlay();
+    }
+    mImageHostOverlay->UseOverlaySource(aOverlay, aPictureRect);
+  } else {
+    mImageHostOverlay = nullptr;
+  }
+}
+
 static TimeStamp
 GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
 {
   switch (aBias) {
   case ImageHost::BIAS_NEGATIVE:
     return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
   case ImageHost::BIAS_POSITIVE:
     return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
@@ -259,16 +274,31 @@ ImageHost::Composite(LayerComposite* aLa
                      const nsIntRegion* aVisibleRegion)
 {
   if (!GetCompositor()) {
     // should only happen when a tab is dragged to another window and
     // async-video is still sending frames but we haven't attached the
     // set the new compositor yet.
     return;
   }
+
+  if (mImageHostOverlay) {
+    mImageHostOverlay->Composite(GetCompositor(),
+                                 mFlashCounter,
+                                 aLayer,
+                                 aEffectChain,
+                                 aOpacity,
+                                 aTransform,
+                                 aFilter,
+                                 aClipRect,
+                                 aVisibleRegion);
+    mBias = BIAS_NONE;
+    return;
+  }
+
   int imageIndex = ChooseImageIndex();
   if (imageIndex < 0) {
     return;
   }
 
   if (uint32_t(imageIndex) + 1 < mImages.Length()) {
     GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
   }
@@ -423,16 +453,20 @@ ImageHost::PrintInfo(std::stringstream& 
 
   nsAutoCString pfx(aPrefix);
   pfx += "  ";
   for (auto& img : mImages) {
     aStream << "\n";
     img.mFrontBuffer->PrintInfo(aStream, pfx.get());
     AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
   }
+
+  if (mImageHostOverlay) {
+    mImageHostOverlay->PrintInfo(aStream, aPrefix);
+  }
 }
 
 void
 ImageHost::Dump(std::stringstream& aStream,
                 const char* aPrefix,
                 bool aDumpHtml)
 {
   for (auto& img : mImages) {
@@ -442,26 +476,34 @@ ImageHost::Dump(std::stringstream& aStre
     DumpTextureHost(aStream, img.mFrontBuffer);
     aStream << (aDumpHtml ? " </li></ul> " : " ");
   }
 }
 
 LayerRenderState
 ImageHost::GetRenderState()
 {
+  if (mImageHostOverlay) {
+    return mImageHostOverlay->GetRenderState();
+  }
+
   TimedImage* img = ChooseImage();
   if (img) {
     return img->mFrontBuffer->GetRenderState();
   }
   return LayerRenderState();
 }
 
 already_AddRefed<gfx::DataSourceSurface>
 ImageHost::GetAsSurface()
 {
+  if (mImageHostOverlay) {
+    return nullptr;
+  }
+
   TimedImage* img = ChooseImage();
   if (img) {
     return img->mFrontBuffer->GetAsSurface();
   }
   return nullptr;
 }
 
 bool
@@ -488,16 +530,20 @@ ImageHost::Unlock()
     img->mFrontBuffer->Unlock();
   }
   mLocked = false;
 }
 
 IntSize
 ImageHost::GetImageSize() const
 {
+  if (mImageHostOverlay) {
+    return mImageHostOverlay->GetImageSize();
+  }
+
   const TimedImage* img = ChooseImage();
   if (img) {
     return IntSize(img->mPictureRect.width, img->mPictureRect.height);
   }
   return IntSize();
 }
 
 already_AddRefed<TexturedEffect>
@@ -529,63 +575,65 @@ ImageHost::SetImageContainer(ImageContai
     mImageContainer->mImageHosts.RemoveElement(this);
   }
   mImageContainer = aImageContainer;
   if (mImageContainer) {
     mImageContainer->mImageHosts.AppendElement(this);
   }
 }
 
-#ifdef MOZ_WIDGET_GONK
-ImageHostOverlay::ImageHostOverlay(const TextureInfo& aTextureInfo)
-  : CompositableHost(aTextureInfo)
+ImageHostOverlay::ImageHostOverlay()
 {
+  MOZ_COUNT_CTOR(ImageHostOverlay);
 }
 
 ImageHostOverlay::~ImageHostOverlay()
 {
+  MOZ_COUNT_DTOR(ImageHostOverlay);
 }
 
 void
-ImageHostOverlay::Composite(LayerComposite* aLayer,
+ImageHostOverlay::Composite(Compositor* aCompositor,
+                            uint32_t aFlashCounter,
+                            LayerComposite* aLayer,
                             EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Filter& aFilter,
                             const gfx::Rect& aClipRect,
                             const nsIntRegion* aVisibleRegion)
 {
-  if (!GetCompositor()) {
+  if (mOverlay.handle().type() == OverlayHandle::Tnull_t) {
     return;
   }
 
-  if (mOverlay.handle().type() == OverlayHandle::Tnull_t)
-    return;
   Color hollow(0.0f, 0.0f, 0.0f, 0.0f);
   aEffectChain.mPrimaryEffect = new EffectSolidColor(hollow);
   aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE] = new EffectBlendMode(CompositionOp::OP_SOURCE);
 
   gfx::Rect rect;
   gfx::Rect clipRect(aClipRect.x, aClipRect.y,
                      aClipRect.width, aClipRect.height);
   rect.SetRect(mPictureRect.x, mPictureRect.y,
                mPictureRect.width, mPictureRect.height);
 
-  mCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
-  mCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
-                               rect, aClipRect, aTransform, mFlashCounter);
+  aCompositor->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
+  aCompositor->DrawDiagnostics(DiagnosticFlags::IMAGE | DiagnosticFlags::BIGIMAGE,
+                               rect, aClipRect, aTransform, aFlashCounter);
 }
 
 LayerRenderState
 ImageHostOverlay::GetRenderState()
 {
   LayerRenderState state;
+#ifdef MOZ_WIDGET_GONK
   if (mOverlay.handle().type() == OverlayHandle::Tint32_t) {
     state.SetOverlayId(mOverlay.handle().get_int32_t());
   }
+#endif
   return state;
 }
 
 void
 ImageHostOverlay::UseOverlaySource(OverlaySource aOverlay,
                                    const nsIntRect& aPictureRect)
 {
   mOverlay = aOverlay;
@@ -597,22 +645,21 @@ ImageHostOverlay::GetImageSize() const
 {
   return IntSize(mPictureRect.width, mPictureRect.height);
 }
 
 void
 ImageHostOverlay::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
-  aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
+  aStream << nsPrintfCString("ImageHostOverlay (0x%p)", this).get();
 
   AppendToString(aStream, mPictureRect, " [picture-rect=", "]");
 
   if (mOverlay.handle().type() == OverlayHandle::Tint32_t) {
     nsAutoCString pfx(aPrefix);
     pfx += "  ";
     aStream << nsPrintfCString("Overlay: %d", mOverlay.handle().get_int32_t()).get();
   }
 }
 
-#endif
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -25,16 +25,17 @@
 #include "nscore.h"                     // for nsACString
 
 namespace mozilla {
 namespace layers {
 
 class Compositor;
 struct EffectChain;
 class ImageContainerParent;
+class ImageHostOverlay;
 
 /**
  * ImageHost. Works with ImageClientSingle and ImageClientBuffered
  */
 class ImageHost : public CompositableHost
 {
 public:
   explicit ImageHost(const TextureInfo& aTextureInfo);
@@ -49,16 +50,19 @@ public:
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr) override;
 
   virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
 
   virtual void RemoveTextureHost(TextureHost* aTexture) override;
 
+  virtual void UseOverlaySource(OverlaySource aOverlay,
+                                const gfx::IntRect& aPictureRect) override;
+
   virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
 
   virtual void Attach(Layer* aLayer,
                       Compositor* aCompositor,
                       AttachFlags aFlags = NO_FLAGS) override;
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
@@ -132,45 +136,46 @@ protected:
   int32_t mLastFrameID;
   int32_t mLastProducerID;
   /**
    * Bias to apply to the next frame.
    */
   Bias mBias;
 
   bool mLocked;
+
+  RefPtr<ImageHostOverlay> mImageHostOverlay;
 };
 
-#ifdef MOZ_WIDGET_GONK
-
 /**
- * ImageHostOverlay works with ImageClientOverlay
+ * ImageHostOverlay handles OverlaySource compositing
  */
-class ImageHostOverlay : public CompositableHost {
+class ImageHostOverlay {
+protected:
+  virtual ~ImageHostOverlay();
+
 public:
-  ImageHostOverlay(const TextureInfo& aTextureInfo);
-  ~ImageHostOverlay();
+  NS_INLINE_DECL_REFCOUNTING(ImageHostOverlay)
+  ImageHostOverlay();
 
-  virtual CompositableType GetType() { return mTextureInfo.mCompositableType; }
-
-  virtual void Composite(LayerComposite* aLayer,
+  virtual void Composite(Compositor* aCompositor,
+                         uint32_t aFlashCounter,
+                         LayerComposite* aLayer,
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr) override;
-  virtual LayerRenderState GetRenderState() override;
+                         const nsIntRegion* aVisibleRegion);
+  virtual LayerRenderState GetRenderState();
   virtual void UseOverlaySource(OverlaySource aOverlay,
-                                const gfx::IntRect& aPictureRect) override;
-  virtual gfx::IntSize GetImageSize() const override;
+                                const gfx::IntRect& aPictureRect);
+  virtual gfx::IntSize GetImageSize() const;
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 protected:
   gfx::IntRect mPictureRect;
   OverlaySource mOverlay;
 };
 
-#endif
-
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -45,17 +45,16 @@ ImageLayerComposite::~ImageLayerComposit
   CleanupResources();
 }
 
 bool
 ImageLayerComposite::SetCompositableHost(CompositableHost* aHost)
 {
   switch (aHost->GetType()) {
     case CompositableType::IMAGE:
-    case CompositableType::IMAGE_OVERLAY:
       mImageHost = aHost;
       return true;
     default:
       return false;
   }
 }
 
 void
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1037,17 +1037,17 @@ LayerManagerComposite::RenderToPresentat
   nsScreenGonk* mirrorScreen = mirrorScreenWidget->GetScreen();
   if (!mirrorScreen->GetTopWindows().IsEmpty()) {
     return;
   }
 
   EGLSurface surface = mirrorScreen->GetEGLSurface();
   if (surface == LOCAL_EGL_NO_SURFACE) {
     // Create GLContext
-    RefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget);
+    RefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget, false);
     mirrorScreenWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT,
                                       reinterpret_cast<uintptr_t>(gl.get()));
     surface = mirrorScreen->GetEGLSurface();
     if (surface == LOCAL_EGL_NO_SURFACE) {
       // Failed to create EGLSurface
       return;
     }
   }
--- 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/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -837,16 +837,17 @@ bool
 DXGITextureHostD3D11::Lock()
 {
   if (!GetDevice()) {
     NS_WARNING("trying to lock a TextureHost without a D3D device");
     return false;
   }
   if (!mTextureSource) {
     if (!mTexture && !OpenSharedHandle()) {
+      gfxWindowsPlatform::GetPlatform()->ForceDeviceReset(ForcedDeviceResetReason::OPENSHAREDHANDLE);
       return false;
     }
 
     mTextureSource = new DataTextureSourceD3D11(mFormat, mCompositor, mTexture);
   }
 
   mIsLocked = LockD3DTexture(mTextureSource->GetD3D11Texture());
 
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -212,17 +212,16 @@ CompositableParentManager::ReceiveCompos
         ScheduleComposition(op);
       }
       break;
     }
 #ifdef MOZ_WIDGET_GONK
     case CompositableOperation::TOpUseOverlaySource: {
       const OpUseOverlaySource& op = aEdit.get_OpUseOverlaySource();
       CompositableHost* compositable = AsCompositable(op);
-      MOZ_ASSERT(compositable->GetType() == CompositableType::IMAGE_OVERLAY, "Invalid operation!");
       if (!ValidatePictureRect(op.overlay().size(), op.picture())) {
         return false;
       }
       compositable->UseOverlaySource(op.overlay(), op.picture());
       break;
     }
 #endif
     default: {
--- 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/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -102,32 +102,33 @@ CompositorOGL::CreateContext()
   if (widgetOpenGLContext) {
     GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
     return already_AddRefed<GLContext>(alreadyRefed);
   }
 
 #ifdef XP_WIN
   if (gfxEnv::LayersPreferEGL()) {
     printf_stderr("Trying GL layers...\n");
-    context = gl::GLContextProviderEGL::CreateForWindow(mWidget);
+    context = gl::GLContextProviderEGL::CreateForWindow(mWidget, false);
   }
 #endif
 
   // Allow to create offscreen GL context for main Layer Manager
   if (!context && gfxEnv::LayersPreferOffscreen()) {
     SurfaceCaps caps = SurfaceCaps::ForRGB();
     caps.preserve = false;
     caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == gfxImageFormat::RGB16_565;
 
     context = GLContextProvider::CreateOffscreen(mSurfaceSize,
                                                  caps, CreateContextFlags::REQUIRE_COMPAT_PROFILE);
   }
 
   if (!context) {
-    context = gl::GLContextProvider::CreateForWindow(mWidget);
+    context = gl::GLContextProvider::CreateForWindow(mWidget,
+                gfxPlatform::GetPlatform()->RequiresAcceleratedGLContextForCompositorOGL());
   }
 
   if (!context) {
     NS_WARNING("Failed to create CompositorOGL context");
   }
 
 #ifdef MOZ_WIDGET_GONK
   mWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT,
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -111,18 +111,16 @@ if CONFIG['GNU_CXX']:
     ]
     if CONFIG['CLANG_CXX']:
         CXXFLAGS += [
             '-Wno-implicit-fallthrough',
             '-Wno-inconsistent-missing-override',
             '-Wno-macro-redefined',
             '-Wno-unused-private-field',
         ]
-        # work around inline function linking bug with template arguments
-        SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions']
     else:
         CXXFLAGS += [
             '-Wno-logical-op',
             '-Wno-maybe-uninitialized',
         ]
     if CONFIG['CPU_ARCH'] == 'arm':
         SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
 
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -722,18 +722,16 @@ if CONFIG['GNU_CXX']:
     ]
     if CONFIG['CLANG_CXX']:
         CXXFLAGS += [
             '-Wno-implicit-fallthrough',
             '-Wno-inconsistent-missing-override',
             '-Wno-macro-redefined',
             '-Wno-unused-private-field',
         ]
-        # work around inline function linking bug with template arguments
-        SOURCES['skia/src/gpu/GrResourceCache.cpp'].flags += ['-fkeep-inline-functions']
     else:
         CXXFLAGS += [
             '-Wno-logical-op',
             '-Wno-maybe-uninitialized',
         ]
     if CONFIG['CPU_ARCH'] == 'arm':
         SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer']
 
deleted file mode 100644
--- a/gfx/tests/crashtests/358732-1.xhtml
+++ /dev/null
@@ -1,22 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>
-Testcase bug 358732 - Crash in _moz_cairo_win32_scaled_font_select_font, part 2
-</title>
-<style>
-  svg, #div{border: 10px solid black;}
-</style>
-
-</head>
-<body>
-This should not crash Mozilla within 5 seconds
-  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="position:absolute;left:0px;top:50px;"><g id="g">
-<foreignObject id="rotate" x="0" y="0" width="800" height="800">
-  <iframe xmlns="http://www.w3.org/1999/xhtml" style="width:800px; height: 800px;" src="358732-iframe.html"></iframe>
-</foreignObject>
-</g></svg>
-
-
-
-</body>
-</html>
deleted file mode 100644
--- a/gfx/tests/crashtests/358732-2.svg
+++ /dev/null
@@ -1,94 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg"
- xmlns:html="http://www.w3.org/1999/xhtml"
- xmlns:mathml="http://www.w3.org/1998/Math/MathML"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns:xforms="http://www.w3.org/2002/xforms">&#1593;<svg:foreignObject x="0" y="0" width="100%" height="100%"></svg:foreignObject>
-<html:script><![CDATA[
-/*template*/
-var doc = document;
-if (document.getElementById('content'))
-  doc = document.getElementById('content').contentDocument;
-
-function addfirstletter(){
-var x=doc.createElementNS('http://www.w3.org/1999/xhtml','style');
-x.innerHTML='\
-*::first-letter {float: right; text-transform: uppercase; background-color:red; font-size:600%;}\
-';
-doc.documentElement.appendChild(x);
-}
-
-setTimeout(addfirstletter,200);
-
-/*template*/
-
-/*template*/
-var doc = document;
-if (document.getElementById('content'))
-  doc = document.getElementById('content').contentDocument;
-
-function addfirstline(){
-var x=doc.createElementNS('http://www.w3.org/1999/xhtml','style');
-x.innerHTML='\
-*::first-line { text-transform: uppercase; background-color:green; font-size:110%; height: 110%;}\
-*::after { content:"anonymous text"; float:right;border:3px solid black;text-transform: uppercase;height: 90%;}\
-*::before { content:"before text"; float:right;border:3px solid black;font-size: 10px;width:80%;}\
-';
-doc.documentElement.appendChild(x);
-}
-setTimeout(addfirstline,200);
-
-/*template*/
-/*template*/
-var doc = document;
-if (document.getElementById('content'))
-  doc = document.getElementById('content').contentDocument;
-
-var timers=0;
-function doe(aObj, aNested, aCurrentTimer){
-var temp =0;
-for (var i in aObj) {
-try {
-if (i == 'ordinal')
- continue;
-if (typeof aObj[i] == 'object') {
-  if (aNested >= 1000 || aObj[i] == window.location || aOb[i] == doc.documentElement.boxObject)
-    continue;
-  setTimeout(doe,500, aObj[i], ++aNested, timers);
-  timers++;
-  if (aOb[i] == doc.documentElement.boxObject.firstChild) {
-  alert(i);
-   continue;
-  };
-}
-if (i == 'textContent' || i == 'innerHTML')
-  continue;
-}
-catch(e){}
-try {
- //if (temp == 68 && aNested == 21 && aCurrentTimer >= 116) {
- // alert(i + '-'+ aObj[i]);
- // return;
- // }
- 
-if (typeof aObj[i] == 'function') {
-if (i =='removeChild' || i == 'getBoxObjectFor' || i == 'enableRollup')
-aObj[i](doc.documentElement);
-}
-else {
- aObj[i]= 'on';
-}
- temp+=1;
-}
-catch (e) {
-
-}
-}
-}
-
-setTimeout(doe,0, doc.documentElement, 300);
-
-/*template*/
-
-]]></html:script>
-</svg>
\ No newline at end of file
deleted file mode 100644
--- a/gfx/tests/crashtests/358732-3.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<html>
-<head>
-<title>Testcase 3 - Bug 358732  Crash in _moz_cairo_win32_scaled_font_select_font, part 2</title>
-<script id="script">
-var i=0;
-function doe() {
-var navigator1 = SpecialPowers.wrap(top).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation);
-var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell);
-var docviewer = docShell.contentViewer.QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer);
-docviewer.textZoom=i;
-i=i+0.2;
-if (i>10)
- i = 0;
-}
-setInterval(doe, 50);
-
-</script>
-</head>
-<body>
-text&#8889;&#74746;&#48236;&#79202;&#67671;&#9251;&#14635;<sup>&#8889;&#74746;&#48236;&#79202;&#67671;&#9251;&#14635;
-text
-</body>
-</html>
deleted file mode 100644
--- a/gfx/tests/crashtests/358732-iframe.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<object>&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;
-&#5787;&#5787;&#5787;&#5787;&#5787;
-&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;
-&#5787;&#5787;&#5787;&#5787;
-<i>&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;&#5787;
-
-<script>
-var doc = document;
-if (document.getElementById('content')) {
-  doc = document.getElementById('content').contentDocument;
-}
-
-var docviewer;
-function do_onload() {
-var navigator1 = SpecialPowers.wrap(parent).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIWebNavigation);
-var docShell = navigator1.QueryInterface(SpecialPowers.Ci.nsIDocShell);
-docviewer = docShell.contentViewer;
-
-setTimeout(doe,500, 0.2);
-}
-do_onload();
-
-
-
-function doe(i) {
-netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
- docviewer.textZoom += i;
-
-if (docviewer.textZoom >=4)
-  i = -0.2;
-
-if (docviewer.textZoom <=0)
-  i = 0.2;
-window.status = docviewer.textZoom;
-setTimeout(doe, 100, i);
-}
-</script>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -7,19 +7,16 @@ load 248518-1.html
 load 306649-1.xml
 load 306902-1.xml
 load 333861-1.html
 load 334735-1.html
 load 345576-1.html
 load 345629-1.html
 load 348462-1.html
 load 348462-2.html
-skip load 358732-1.xhtml # Bug 1226751
-load 358732-2.svg
-load 358732-3.html
 load 366643.html
 load 369688-1.html
 load 369947-1.html
 load 372094-1.xhtml
 load 376627-1.html
 load 377231-1.html
 load 377232-1.xhtml
 load 377461-1.xhtml
--- 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
@@ -2241,19 +2242,17 @@ gfxPlatform::GetAcceleratedCompositorBac
 }
 
 void
 gfxPlatform::GetCompositorBackends(bool useAcceleration, nsTArray<mozilla::layers::LayersBackend>& aBackends)
 {
   if (useAcceleration) {
     GetAcceleratedCompositorBackends(aBackends);
   }
-  if (SupportsBasicCompositor()) {
-    aBackends.AppendElement(LayersBackend::LAYERS_BASIC);
-  }
+  aBackends.AppendElement(LayersBackend::LAYERS_BASIC);
 }
 
 void
 gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend)
 {
   if (mCompositorBackend == aBackend) {
     return;
   }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -127,19 +127,25 @@ enum class DeviceResetReason
 {
   OK = 0,
   HUNG,
   REMOVED,
   RESET,
   DRIVER_ERROR,
   INVALID_CALL,
   OUT_OF_MEMORY,
+  FORCED_RESET,
   UNKNOWN
 };
 
+enum class ForcedDeviceResetReason
+{
+  OPENSHAREDHANDLE = 0
+};
+
 class gfxPlatform {
     friend class SRGBOverrideObserver;
 
 public:
     typedef mozilla::gfx::Color Color;
     typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::gfx::IntSize IntSize;
@@ -639,36 +645,37 @@ public:
     // devices. Currently this is only used on Windows.
     virtual void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut);
 
     // Plugin async drawing support.
     virtual bool SupportsPluginDirectBitmapDrawing() {
       return false;
     }
 
+    // Some platforms don't support CompositorOGL in an unaccelerated OpenGL
+    // context. These platforms should return true here.
+    virtual bool RequiresAcceleratedGLContextForCompositorOGL() const {
+      return false;
+    }
+
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
     /**
      * Initialized hardware vsync based on each platform.
      */
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource();
 
     // Returns whether or not layers should be accelerated by default on this platform.
     virtual bool AccelerateLayersByDefault();
 
     // Returns a prioritized list of available compositor backends for acceleration.
     virtual void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
 
-    // Returns whether or not the basic compositor is supported.
-    virtual bool SupportsBasicCompositor() const {
-      return true;
-    }
-
     /**
      * Initialise the preferred and fallback canvas backends
      * aBackendBitmask specifies the backends which are acceptable to the caller.
      * The backend used is determined by aBackendBitmask and the order specified
      * by the gfx.canvas.azure.backends pref.
      */
     void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault,
                           uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault);
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -78,19 +78,21 @@ public:
     virtual bool CanRenderContentToDataSurface() const override {
       return true;
     }
 
     virtual bool SupportsApzWheelInput() const override {
       return true;
     }
 
-    bool SupportsBasicCompositor() const override {
-      // At the moment, BasicCompositor is broken on mac.
-      return false;
+    bool RequiresAcceleratedGLContextForCompositorOGL() const override {
+      // On OS X in a VM, unaccelerated CompositorOGL shows black flashes, so we
+      // require accelerated GL for CompositorOGL but allow unaccelerated GL for
+      // BasicCompositor.
+      return true;
     }
 
     bool UseAcceleratedCanvas();
 
     virtual bool UseProgressivePaint() override;
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
 
     // lower threshold on font anti-aliasing
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -550,16 +550,25 @@ gfxWindowsPlatform::UpdateRenderMode()
   UpdateBackendPrefs();
 
   if (didReset) {
     mScreenReferenceDrawTarget =
       CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
   }
 }
 
+void
+gfxWindowsPlatform::ForceDeviceReset(ForcedDeviceResetReason aReason)
+{
+  Telemetry::Accumulate(Telemetry::FORCED_DEVICE_RESET_REASON, uint32_t(aReason));
+
+  mDeviceResetReason = DeviceResetReason::FORCED_RESET;
+  mHasDeviceReset = true;
+}
+
 mozilla::gfx::BackendType
 gfxWindowsPlatform::GetContentBackendFor(mozilla::layers::LayersBackend aLayers)
 {
   if (aLayers == LayersBackend::LAYERS_D3D11) {
     return gfxPlatform::GetDefaultContentBackend();
   }
 
   // If we're not accelerated with D3D11, never use D2D.
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -145,16 +145,21 @@ public:
 
     /**
      * Updates render mode with relation to the current preferences and
      * available devices.
      */
     void UpdateRenderMode();
 
     /**
+     * Forces all GPU resources to be recreated on the next frame.
+     */
+    void ForceDeviceReset(ForcedDeviceResetReason aReason);
+
+    /**
      * Verifies a D2D device is present and working, will attempt to create one
      * it is non-functional or non-existant.
      *
      * \param aAttemptForce Attempt to force D2D cairo device creation by using
      * cairo device creation routines.
      */
     void VerifyD2DDevice(bool aAttemptForce);
 
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/image/Downscaler.h
+++ b/image/Downscaler.h
@@ -53,16 +53,17 @@ class Downscaler
 {
 public:
   /// Constructs a new Downscaler which to scale to size @aTargetSize.
   explicit Downscaler(const nsIntSize& aTargetSize);
   ~Downscaler();
 
   const nsIntSize& OriginalSize() const { return mOriginalSize; }
   const nsIntSize& TargetSize() const { return mTargetSize; }
+  const nsIntSize FrameSize() const { return nsIntSize(mFrameRect.width, mFrameRect.height); }
   const gfxSize& Scale() const { return mScale; }
 
   /**
    * Begins a new frame and reinitializes the Downscaler.
    *
    * @param aOriginalSize The original size of this frame, before scaling.
    * @param aFrameRect The region of  the original image which has data.
    *                   Every pixel outside @aFrameRect is considered blank and
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -1172,17 +1172,17 @@ nsGIFDecoder2::WriteInternal(const char*
           }
         }
       }
 
       if (q[8] & 0x40) {
         mGIFStruct.interlaced = true;
         mGIFStruct.ipass = 1;
         if (mDownscaler) {
-          mDeinterlacer.emplace(mDownscaler->OriginalSize());
+          mDeinterlacer.emplace(mDownscaler->FrameSize());
         }
       } else {
         mGIFStruct.interlaced = false;
         mGIFStruct.ipass = 0;
       }
 
       // Only apply the Haeberli display hack on the first frame
       mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
--- a/intl/uconv/ucvja/nsJapaneseToUnicode.cpp
+++ b/intl/uconv/ucvja/nsJapaneseToUnicode.cpp
@@ -331,16 +331,17 @@ NS_IMETHODIMP nsEUCJPToUnicodeV2::Conver
             if(0xFF != off) {
               *dest++ = gJapaneseMap[mData+off];
               mState = 0;
               if(dest >= destEnd)
                 goto error1;
               break;
             }
             // else fall through to error handler
+            MOZ_FALLTHROUGH;
           }
           case 5: // two bytes undefined
           {
             if (mErrBehavior == kOnError_Signal)
               goto error_invalidchar;
             *dest++ = 0xFFFD;
             // Undefined JIS 0212 two byte sequence. If the second byte is in
             // the valid range for a two byte sequence (0xa1 - 0xfe) consume
@@ -366,18 +367,16 @@ error1:
    src++;
    if ((mState == 0) && (src == srcEnd)) {
      return NS_OK;
    } 
    *aSrcLen = src - (const unsigned char*)aSrc;
    return NS_OK_UDEC_MOREOUTPUT;
 }
 
-
-
 NS_IMETHODIMP nsISO2022JPToUnicodeV2::Convert(
    const char * aSrc, int32_t * aSrcLen,
      char16_t * aDest, int32_t * aDestLen)
 {
    static const uint16_t fbIdx[128] =
    {
 /* 0x8X */
      0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -14,22 +14,24 @@
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 
 struct JSRuntime;
 class JSScript;
 
 namespace js {
     class Activation;
-    class AsmJSProfilingFrameIterator;
     namespace jit {
         class JitActivation;
         class JitProfilingFrameIterator;
         class JitcodeGlobalEntry;
     } // namespace jit
+    namespace wasm {
+        class ProfilingFrameIterator;
+    } // namespace wasm
 } // namespace js
 
 namespace JS {
 
 struct ForEachTrackedOptimizationAttemptOp;
 struct ForEachTrackedOptimizationTypeInfoOp;
 
 // This iterator can be used to walk the stack of a thread suspended at an
@@ -44,25 +46,25 @@ class JS_PUBLIC_API(ProfilingFrameIterat
 
     // When moving past a JitActivation, we need to save the prevJitTop
     // from it to use as the exit-frame pointer when the next caller jit
     // activation (if any) comes around.
     void* savedPrevJitTop_;
 
     static const unsigned StorageSpace = 8 * sizeof(void*);
     mozilla::AlignedStorage<StorageSpace> storage_;
-    js::AsmJSProfilingFrameIterator& asmJSIter() {
+    js::wasm::ProfilingFrameIterator& asmJSIter() {
         MOZ_ASSERT(!done());
         MOZ_ASSERT(isAsmJS());
-        return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr());
+        return *reinterpret_cast<js::wasm::ProfilingFrameIterator*>(storage_.addr());
     }
-    const js::AsmJSProfilingFrameIterator& asmJSIter() const {
+    const js::wasm::ProfilingFrameIterator& asmJSIter() const {
         MOZ_ASSERT(!done());
         MOZ_ASSERT(isAsmJS());
-        return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr());
+        return *reinterpret_cast<const js::wasm::ProfilingFrameIterator*>(storage_.addr());
     }
 
     js::jit::JitProfilingFrameIterator& jitIter() {
         MOZ_ASSERT(!done());
         MOZ_ASSERT(isJit());
         return *reinterpret_cast<js::jit::JitProfilingFrameIterator*>(storage_.addr());
     }
 
rename from js/src/asmjs/AsmJSValidate.cpp
rename to js/src/asmjs/AsmJS.cpp
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -11,54 +11,756 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#include "asmjs/AsmJSValidate.h"
-
-#include "mozilla/Move.h"
-#include "mozilla/UniquePtr.h"
+#include "asmjs/AsmJS.h"
+
+#include "mozilla/Compression.h"
+#include "mozilla/MathAlgorithms.h"
 
 #include "jsmath.h"
 #include "jsprf.h"
 #include "jsutil.h"
 
-#include "asmjs/AsmJSLink.h"
-#include "asmjs/AsmJSModule.h"
+#include "jswrapper.h"
+
 #include "asmjs/WasmGenerator.h"
+#include "asmjs/WasmSerialize.h"
 #include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "jit/AtomicOperations.h"
 #include "jit/MIR.h"
+#include "js/Class.h"
+#include "js/MemoryMetrics.h"
+#include "vm/StringBuffer.h"
 #include "vm/Time.h"
+#include "vm/TypedArrayObject.h"
 
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "frontend/Parser-inl.h"
+#include "vm/ArrayBufferObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 using namespace js::jit;
 using namespace js::wasm;
 
+using mozilla::Compression::LZ4;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
+using mozilla::MallocSizeOf;
 using mozilla::Move;
+using mozilla::PodCopy;
+using mozilla::PodEqual;
+using mozilla::PodZero;
 using mozilla::PositiveInfinity;
-using mozilla::UniquePtr;
 using JS::AsmJSOption;
 using JS::GenericNaN;
 
 /*****************************************************************************/
+// asm.js module object
+
+// The asm.js spec recognizes this set of builtin Math functions.
+enum AsmJSMathBuiltinFunction
+{
+    AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
+    AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
+    AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
+    AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
+    AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
+    AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max,
+    AsmJSMathBuiltin_clz32
+};
+
+// The asm.js spec will recognize this set of builtin Atomics functions.
+enum AsmJSAtomicsBuiltinFunction
+{
+    AsmJSAtomicsBuiltin_compareExchange,
+    AsmJSAtomicsBuiltin_exchange,
+    AsmJSAtomicsBuiltin_load,
+    AsmJSAtomicsBuiltin_store,
+    AsmJSAtomicsBuiltin_fence,
+    AsmJSAtomicsBuiltin_add,
+    AsmJSAtomicsBuiltin_sub,
+    AsmJSAtomicsBuiltin_and,
+    AsmJSAtomicsBuiltin_or,
+    AsmJSAtomicsBuiltin_xor,
+    AsmJSAtomicsBuiltin_isLockFree
+};
+
+// Set of known global object SIMD's attributes, i.e. types
+enum AsmJSSimdType
+{
+    AsmJSSimdType_int32x4,
+    AsmJSSimdType_float32x4,
+    AsmJSSimdType_bool32x4
+};
+
+static inline bool
+IsSignedIntSimdType(AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4:
+        return true;
+      case AsmJSSimdType_float32x4:
+      case AsmJSSimdType_bool32x4:
+        return false;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown SIMD type");
+}
+
+// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
+enum AsmJSSimdOperation
+{
+#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
+    FORALL_SIMD_ASMJS_OP(ASMJSSIMDOPERATION)
+#undef ASMJSSIMDOPERATION
+};
+
+// An AsmJSModule extends (via containment) a wasm::Module with the extra
+// persistent state necessary to represent a compiled asm.js module.
+class js::AsmJSModule
+{
+  public:
+    class Global
+    {
+      public:
+        enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
+                     AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
+        enum VarInitKind { InitConstant, InitImport };
+        enum ConstantKind { GlobalConstant, MathConstant };
+
+      private:
+        struct CacheablePod {
+            Which which_;
+            union {
+                struct {
+                    uint32_t globalDataOffset_;
+                    VarInitKind initKind_;
+                    union {
+                        wasm::ValType importType_;
+                        wasm::Val val_;
+                    } u;
+                } var;
+                uint32_t ffiIndex_;
+                Scalar::Type viewType_;
+                AsmJSMathBuiltinFunction mathBuiltinFunc_;
+                AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
+                AsmJSSimdType simdCtorType_;
+                struct {
+                    AsmJSSimdType type_;
+                    AsmJSSimdOperation which_;
+                } simdOp;
+                struct {
+                    ConstantKind kind_;
+                    double value_;
+                } constant;
+            } u;
+        } pod;
+        PropertyName* name_;
+
+        friend class AsmJSModule;
+
+        Global(Which which, PropertyName* name) {
+            mozilla::PodZero(&pod);  // zero padding for Valgrind
+            pod.which_ = which;
+            name_ = name;
+            MOZ_ASSERT_IF(name_, name_->isTenured());
+        }
+
+        void trace(JSTracer* trc) {
+            if (name_)
+                TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
+        }
+
+      public:
+        Global() {}
+        Which which() const {
+            return pod.which_;
+        }
+        uint32_t varGlobalDataOffset() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            return pod.u.var.globalDataOffset_;
+        }
+        VarInitKind varInitKind() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            return pod.u.var.initKind_;
+        }
+        wasm::Val varInitVal() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
+            return pod.u.var.u.val_;
+        }
+        wasm::ValType varInitImportType() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+            return pod.u.var.u.importType_;
+        }
+        PropertyName* varImportField() const {
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+            return name_;
+        }
+        PropertyName* ffiField() const {
+            MOZ_ASSERT(pod.which_ == FFI);
+            return name_;
+        }
+        uint32_t ffiIndex() const {
+            MOZ_ASSERT(pod.which_ == FFI);
+            return pod.u.ffiIndex_;
+        }
+        // When a view is created from an imported constructor:
+        //   var I32 = stdlib.Int32Array;
+        //   var i32 = new I32(buffer);
+        // the second import has nothing to validate and thus has a null field.
+        PropertyName* maybeViewName() const {
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+            return name_;
+        }
+        Scalar::Type viewType() const {
+            MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+            return pod.u.viewType_;
+        }
+        PropertyName* mathName() const {
+            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+            return name_;
+        }
+        PropertyName* atomicsName() const {
+            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+            return name_;
+        }
+        AsmJSMathBuiltinFunction mathBuiltinFunction() const {
+            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+            return pod.u.mathBuiltinFunc_;
+        }
+        AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
+            MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+            return pod.u.atomicsBuiltinFunc_;
+        }
+        AsmJSSimdType simdCtorType() const {
+            MOZ_ASSERT(pod.which_ == SimdCtor);
+            return pod.u.simdCtorType_;
+        }
+        PropertyName* simdCtorName() const {
+            MOZ_ASSERT(pod.which_ == SimdCtor);
+            return name_;
+        }
+        PropertyName* simdOperationName() const {
+            MOZ_ASSERT(pod.which_ == SimdOperation);
+            return name_;
+        }
+        AsmJSSimdOperation simdOperation() const {
+            MOZ_ASSERT(pod.which_ == SimdOperation);
+            return pod.u.simdOp.which_;
+        }
+        AsmJSSimdType simdOperationType() const {
+            MOZ_ASSERT(pod.which_ == SimdOperation);
+            return pod.u.simdOp.type_;
+        }
+        PropertyName* constantName() const {
+            MOZ_ASSERT(pod.which_ == Constant);
+            return name_;
+        }
+        ConstantKind constantKind() const {
+            MOZ_ASSERT(pod.which_ == Constant);
+            return pod.u.constant.kind_;
+        }
+        double constantValue() const {
+            MOZ_ASSERT(pod.which_ == Constant);
+            return pod.u.constant.value_;
+        }
+
+        WASM_DECLARE_SERIALIZABLE(Global);
+    };
+
+    typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
+
+    // An import is slightly different than an asm.js FFI function: a single
+    // asm.js FFI function can be called with many different signatures. When
+    // compiled to wasm, each unique FFI function paired with signature
+    // generates a wasm import.
+    class Import
+    {
+        uint32_t ffiIndex_;
+      public:
+        Import() = default;
+        explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
+        uint32_t ffiIndex() const { return ffiIndex_; }
+    };
+
+    typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
+
+    class Export
+    {
+        PropertyName* name_;
+        PropertyName* maybeFieldName_;
+        struct CacheablePod {
+            uint32_t wasmIndex_;
+            uint32_t startOffsetInModule_;  // Store module-start-relative offsets
+            uint32_t endOffsetInModule_;    // so preserved by serialization.
+        } pod;
+
+      public:
+        Export() {}
+        Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+               uint32_t startOffsetInModule, uint32_t endOffsetInModule)
+          : name_(name),
+            maybeFieldName_(maybeFieldName)
+        {
+            MOZ_ASSERT(name_->isTenured());
+            MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
+            pod.wasmIndex_ = wasmIndex;
+            pod.startOffsetInModule_ = startOffsetInModule;
+            pod.endOffsetInModule_ = endOffsetInModule;
+        }
+
+        void trace(JSTracer* trc) {
+            TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
+            if (maybeFieldName_)
+                TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
+        }
+
+        PropertyName* name() const {
+            return name_;
+        }
+        PropertyName* maybeFieldName() const {
+            return maybeFieldName_;
+        }
+        uint32_t startOffsetInModule() const {
+            return pod.startOffsetInModule_;
+        }
+        uint32_t endOffsetInModule() const {
+            return pod.endOffsetInModule_;
+        }
+        static const uint32_t ChangeHeap = UINT32_MAX;
+        bool isChangeHeap() const {
+            return pod.wasmIndex_ == ChangeHeap;
+        }
+        uint32_t wasmIndex() const {
+            MOZ_ASSERT(!isChangeHeap());
+            return pod.wasmIndex_;
+        }
+
+        WASM_DECLARE_SERIALIZABLE(Export)
+    };
+
+    typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
+
+    typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
+
+  private:
+    UniqueWasmModule            wasmModule_;
+    wasm::UniqueStaticLinkData  linkData_;
+    struct CacheablePod {
+        uint32_t                minHeapLength_;
+        uint32_t                maxHeapLength_;
+        uint32_t                heapLengthMask_;
+        uint32_t                numFFIs_;
+        uint32_t                srcLength_;
+        uint32_t                srcLengthWithRightBrace_;
+        bool                    strict_;
+        bool                    hasArrayView_;
+        bool                    isSharedView_;
+        bool                    hasFixedMinHeapLength_;
+    } pod;
+    const ScriptSourceHolder    scriptSource_;
+    const uint32_t              srcStart_;
+    const uint32_t              srcBodyStart_;
+    GlobalVector                globals_;
+    ImportVector                imports_;
+    ExportVector                exports_;
+    PropertyName*               globalArgumentName_;
+    PropertyName*               importArgumentName_;
+    PropertyName*               bufferArgumentName_;
+
+  public:
+    explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
+                         bool strict)
+      : scriptSource_(scriptSource),
+        srcStart_(srcStart),
+        srcBodyStart_(srcBodyStart),
+        globalArgumentName_(nullptr),
+        importArgumentName_(nullptr),
+        bufferArgumentName_(nullptr)
+    {
+        mozilla::PodZero(&pod);
+        pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
+        pod.maxHeapLength_ = 0x80000000;
+        pod.strict_ = strict;
+
+        MOZ_ASSERT(srcStart_ <= srcBodyStart_);
+
+        // AsmJSCheckedImmediateRange should be defined to be at most the minimum
+        // heap length so that offsets can be folded into bounds checks.
+        MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_);
+    }
+
+    void trace(JSTracer* trc) {
+        if (wasmModule_)
+            wasmModule_->trace(trc);
+        for (Global& global : globals_)
+            global.trace(trc);
+        for (Export& exp : exports_)
+            exp.trace(trc);
+        if (globalArgumentName_)
+            TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
+        if (importArgumentName_)
+            TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
+        if (bufferArgumentName_)
+            TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
+    }
+
+    /*************************************************************************/
+    // These functions may be used as soon as the module is constructed:
+
+    ScriptSource* scriptSource() const {
+        return scriptSource_.get();
+    }
+    bool strict() const {
+        return pod.strict_;
+    }
+
+    // srcStart() refers to the offset in the ScriptSource to the beginning of
+    // the asm.js module function. If the function has been created with the
+    // Function constructor, this will be the first character in the function
+    // source. Otherwise, it will be the opening parenthesis of the arguments
+    // list.
+    uint32_t srcStart() const {
+        return srcStart_;
+    }
+
+    // srcBodyStart() refers to the offset in the ScriptSource to the end
+    // of the 'use asm' string-literal token.
+    uint32_t srcBodyStart() const {
+        return srcBodyStart_;
+    }
+
+    // While these functions may be accessed at any time, their values will
+    // change as the module is compiled.
+    uint32_t minHeapLength() const {
+        return pod.minHeapLength_;
+    }
+    uint32_t maxHeapLength() const {
+        return pod.maxHeapLength_;
+    }
+    uint32_t heapLengthMask() const {
+        MOZ_ASSERT(pod.hasFixedMinHeapLength_);
+        return pod.heapLengthMask_;
+    }
+
+    void initGlobalArgumentName(PropertyName* n) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT_IF(n, n->isTenured());
+        globalArgumentName_ = n;
+    }
+    void initImportArgumentName(PropertyName* n) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT_IF(n, n->isTenured());
+        importArgumentName_ = n;
+    }
+    void initBufferArgumentName(PropertyName* n) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT_IF(n, n->isTenured());
+        bufferArgumentName_ = n;
+    }
+    PropertyName* globalArgumentName() const {
+        return globalArgumentName_;
+    }
+    PropertyName* importArgumentName() const {
+        return importArgumentName_;
+    }
+    PropertyName* bufferArgumentName() const {
+        return bufferArgumentName_;
+    }
+
+    bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Variable, nullptr);
+        g.pod.u.var.initKind_ = Global::InitConstant;
+        g.pod.u.var.u.val_ = v;
+        g.pod.u.var.globalDataOffset_ = globalDataOffset;
+        return globals_.append(g);
+    }
+    bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Variable, name);
+        g.pod.u.var.initKind_ = Global::InitImport;
+        g.pod.u.var.u.importType_ = importType;
+        g.pod.u.var.globalDataOffset_ = globalDataOffset;
+        return globals_.append(g);
+    }
+    // See Import comment above for FFI vs. Import.
+    bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
+        MOZ_ASSERT(!isFinished());
+        if (pod.numFFIs_ == UINT32_MAX)
+            return false;
+        Global g(Global::FFI, field);
+        g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
+        return globals_.append(g);
+    }
+    bool addArrayView(Scalar::Type vt, PropertyName* maybeField) {
+        MOZ_ASSERT(!isFinished());
+        pod.hasArrayView_ = true;
+        pod.isSharedView_ = false;
+        Global g(Global::ArrayView, maybeField);
+        g.pod.u.viewType_ = vt;
+        return globals_.append(g);
+    }
+    bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(field);
+        pod.isSharedView_ = false;
+        Global g(Global::ArrayViewCtor, field);
+        g.pod.u.viewType_ = vt;
+        return globals_.append(g);
+    }
+    bool addByteLength() {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::ByteLength, nullptr);
+        return globals_.append(g);
+    }
+    bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::MathBuiltinFunction, field);
+        g.pod.u.mathBuiltinFunc_ = func;
+        return globals_.append(g);
+    }
+    bool addMathBuiltinConstant(double value, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Constant, field);
+        g.pod.u.constant.value_ = value;
+        g.pod.u.constant.kind_ = Global::MathConstant;
+        return globals_.append(g);
+    }
+    bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::AtomicsBuiltinFunction, field);
+        g.pod.u.atomicsBuiltinFunc_ = func;
+        return globals_.append(g);
+    }
+    bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::SimdCtor, field);
+        g.pod.u.simdCtorType_ = type;
+        return globals_.append(g);
+    }
+    bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::SimdOperation, field);
+        g.pod.u.simdOp.type_ = type;
+        g.pod.u.simdOp.which_ = op;
+        return globals_.append(g);
+    }
+    bool addGlobalConstant(double value, PropertyName* name) {
+        MOZ_ASSERT(!isFinished());
+        Global g(Global::Constant, name);
+        g.pod.u.constant.value_ = value;
+        g.pod.u.constant.kind_ = Global::GlobalConstant;
+        return globals_.append(g);
+    }
+    // See Import comment above for FFI vs. Import.
+    bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
+        MOZ_ASSERT(imports_.length() == importIndex);
+        return imports_.emplaceBack(ffiIndex);
+    }
+    bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+                   uint32_t funcSrcBegin, uint32_t funcSrcEnd)
+    {
+        // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
+        // (the entire file) and ExportedFunctions store offsets relative to
+        // the beginning of the module (so that they are caching-invariant).
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(srcStart_ < funcSrcBegin);
+        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
+        return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
+                                    funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
+    }
+    bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
+        MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
+        MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
+        MOZ_ASSERT(max <= pod.maxHeapLength_);
+        MOZ_ASSERT(min <= max);
+        pod.heapLengthMask_ = mask;
+        pod.minHeapLength_ = min;
+        pod.maxHeapLength_ = max;
+        pod.hasFixedMinHeapLength_ = true;
+        return true;
+    }
+
+    const GlobalVector& globals() const {
+        return globals_;
+    }
+    const ImportVector& imports() const {
+        return imports_;
+    }
+    const ExportVector& exports() const {
+        return exports_;
+    }
+
+    void setViewsAreShared() {
+        if (pod.hasArrayView_)
+            pod.isSharedView_ = true;
+    }
+    bool hasArrayView() const {
+        return pod.hasArrayView_;
+    }
+    bool isSharedView() const {
+        return pod.isSharedView_;
+    }
+    bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
+        MOZ_ASSERT(!isFinished());
+        if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
+            return false;
+        if (len > pod.maxHeapLength_)
+            return false;
+        len = RoundUpToNextValidAsmJSHeapLength(len);
+        if (len > pod.minHeapLength_)
+            pod.minHeapLength_ = len;
+        return true;
+    }
+
+    /*************************************************************************/
+    // A module isFinished() when compilation completes. After being finished,
+    // a module must be statically and dynamically linked before execution.
+
+    bool isFinished() const {
+        return !!wasmModule_;
+    }
+    void finish(wasm::Module* wasmModule, wasm::UniqueStaticLinkData linkData,
+                uint32_t endBeforeCurly, uint32_t endAfterCurly)
+    {
+        MOZ_ASSERT(!isFinished());
+
+        wasmModule_.reset(wasmModule);
+        linkData_ = Move(linkData);
+
+        MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
+        MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
+        pod.srcLength_ = endBeforeCurly - srcStart_;
+        pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
+
+        MOZ_ASSERT(isFinished());
+    }
+
+    /*************************************************************************/
+    // These accessor functions can only be used after finish():
+
+    wasm::Module& wasmModule() const {
+        MOZ_ASSERT(isFinished());
+        return *wasmModule_;
+    }
+    uint32_t numFFIs() const {
+        MOZ_ASSERT(isFinished());
+        return pod.numFFIs_;
+    }
+    uint32_t srcEndBeforeCurly() const {
+        MOZ_ASSERT(isFinished());
+        return srcStart_ + pod.srcLength_;
+    }
+    uint32_t srcEndAfterCurly() const {
+        MOZ_ASSERT(isFinished());
+        return srcStart_ + pod.srcLengthWithRightBrace_;
+    }
+    bool staticallyLink(ExclusiveContext* cx) {
+        return wasmModule_->staticallyLink(cx, *linkData_);
+    }
+
+    // See WASM_DECLARE_SERIALIZABLE.
+    size_t serializedSize() const;
+    uint8_t* serialize(uint8_t* cursor) const;
+    const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
+    bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const;
+    void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
+};
+
+static void
+AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj)
+{
+    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
+    if (moduleObj.hasModule())
+        fop->delete_(&moduleObj.module());
+}
+
+static void
+AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
+{
+    AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
+    if (moduleObj.hasModule())
+        moduleObj.module().trace(trc);
+}
+
+const Class AsmJSModuleObject::class_ = {
+    "AsmJSModuleObject",
+    JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
+    JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    AsmJSModuleObject_finalize,
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    AsmJSModuleObject_trace
+};
+
+static AsmJSModuleObject*
+NewAsmJSModuleObject(ExclusiveContext* cx)
+{
+    AutoSetNewObjectMetadata metadata(cx);
+    JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr);
+    if (!obj)
+        return nullptr;
+
+    return &obj->as<AsmJSModuleObject>();
+}
+
+bool
+AsmJSModuleObject::hasModule() const
+{
+    MOZ_ASSERT(is<AsmJSModuleObject>());
+    return !getReservedSlot(MODULE_SLOT).isUndefined();
+}
+
+void
+AsmJSModuleObject::setModule(AsmJSModule* newModule)
+{
+    MOZ_ASSERT(is<AsmJSModuleObject>());
+    if (hasModule())
+        js_delete(&module());
+    setReservedSlot(MODULE_SLOT, PrivateValue(newModule));
+}
+
+AsmJSModule&
+AsmJSModuleObject::module() const
+{
+    MOZ_ASSERT(is<AsmJSModuleObject>());
+    return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate();
+}
+
+void
+AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+    module().addSizeOfMisc(mallocSizeOf, code, data);
+}
+
+/*****************************************************************************/
 // ParseNode utilities
 
 static inline ParseNode*
 NextNode(ParseNode* pn)
 {
     return pn->pn_next;
 }
 
@@ -535,28 +1237,28 @@ class NumLit
         Bool32x4,
         OutOfRangeInt = -1
     };
 
   private:
     Which which_;
     union {
         Value scalar_;
-        jit::SimdConstant simd_;
+        SimdConstant simd_;
     } u;
 
   public:
     NumLit() = default;
 
     NumLit(Which w, Value v) : which_(w) {
         u.scalar_ = v;
         MOZ_ASSERT(!isSimd());
     }
 
-    NumLit(Which w, jit::SimdConstant c) : which_(w) {
+    NumLit(Which w, SimdConstant c) : which_(w) {
         u.simd_ = c;
         MOZ_ASSERT(isSimd());
     }
 
     Which which() const {
         return which_;
     }
 
@@ -838,40 +1540,40 @@ class Type
             return ValType::F32;
         else if (isDouble())
             return ValType::F64;
         else if (isInt32x4())
             return ValType::I32x4;
         return ValType::F32x4;
     }
 
-    jit::MIRType toMIRType() const {
+    MIRType toMIRType() const {
         switch (which_) {
           case Double:
           case DoubleLit:
           case MaybeDouble:
-            return jit::MIRType_Double;
+            return MIRType_Double;
           case Float:
           case Floatish:
           case MaybeFloat:
-            return jit::MIRType_Float32;
+            return MIRType_Float32;
           case Fixnum:
           case Int:
           case Signed:
           case Unsigned:
           case Intish:
-            return jit::MIRType_Int32;
+            return MIRType_Int32;
           case Int32x4:
-            return jit::MIRType_Int32x4;
+            return MIRType_Int32x4;
           case Float32x4:
-            return jit::MIRType_Float32x4;
+            return MIRType_Float32x4;
           case Bool32x4:
-            return jit::MIRType_Bool32x4;
+            return MIRType_Bool32x4;
           case Void:
-            return jit::MIRType_None;
+            return MIRType_None;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
     }
 
     AsmJSSimdType simdType() const {
         MOZ_ASSERT(isSimd());
         switch (which_) {
           case Int32x4:
@@ -1159,23 +1861,23 @@ class MOZ_STACK_CLASS ModuleValidator
         ArrayView(PropertyName* name, Scalar::Type type)
           : name(name), type(type)
         {}
 
         PropertyName* name;
         Scalar::Type type;
     };
 
-    class ExitDescriptor
+    class ImportDescriptor
     {
         PropertyName* name_;
         const LifoSig* sig_;
 
       public:
-        ExitDescriptor(PropertyName* name, const LifoSig& sig)
+        ImportDescriptor(PropertyName* name, const LifoSig& sig)
           : name_(name), sig_(&sig)
         {}
 
         PropertyName* name() const {
             return name_;
         }
         const LifoSig& sig() const {
             return *sig_;
@@ -1184,71 +1886,72 @@ class MOZ_STACK_CLASS ModuleValidator
         struct Lookup {  // implements HashPolicy
             PropertyName* name_;
             const MallocSig& sig_;
             Lookup(PropertyName* name, const MallocSig& sig) : name_(name), sig_(sig) {}
         };
         static HashNumber hash(const Lookup& l) {
             return HashGeneric(l.name_, l.sig_.hash());
         }
-        static bool match(const ExitDescriptor& lhs, const Lookup& rhs) {
+        static bool match(const ImportDescriptor& lhs, const Lookup& rhs) {
             return lhs.name_ == rhs.name_ && *lhs.sig_ == rhs.sig_;
         }
     };
 
   private:
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
     typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
     typedef Vector<ArrayView> ArrayViewVector;
 
   public:
-    typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
+    typedef HashMap<ImportDescriptor, unsigned, ImportDescriptor> ImportMap;
 
   private:
-    ExclusiveContext*                       cx_;
-    AsmJSParser&                            parser_;
-
-    ModuleGenerator                         mg_;
-
-    LifoAlloc                               validationLifo_;
-    FuncVector                              functions_;
-    FuncPtrTableVector                      funcPtrTables_;
-    GlobalMap                               globals_;
-    ArrayViewVector                         arrayViews_;
-    ExitMap                                 exits_;
-
-    MathNameMap                             standardLibraryMathNames_;
-    AtomicsNameMap                          standardLibraryAtomicsNames_;
-    SimdOperationNameMap                    standardLibrarySimdOpNames_;
-
-    ParseNode*                              moduleFunctionNode_;
-    PropertyName*                           moduleFunctionName_;
-
-    UniquePtr<char[], JS::FreePolicy>       errorString_;
-    uint32_t                                errorOffset_;
-    bool                                    errorOverRecursed_;
-
-    bool                                    canValidateChangeHeap_;
-    bool                                    hasChangeHeap_;
-    bool                                    supportsSimd_;
-    bool                                    atomicsPresent_;
+    ExclusiveContext*    cx_;
+    AsmJSParser&         parser_;
+
+    ModuleGenerator      mg_;
+    AsmJSModule*         module_;
+
+    LifoAlloc            validationLifo_;
+    FuncVector           functions_;
+    FuncPtrTableVector   funcPtrTables_;
+    GlobalMap            globals_;
+    ArrayViewVector      arrayViews_;
+    ImportMap            imports_;
+
+    MathNameMap          standardLibraryMathNames_;
+    AtomicsNameMap       standardLibraryAtomicsNames_;
+    SimdOperationNameMap standardLibrarySimdOpNames_;
+
+    ParseNode*           moduleFunctionNode_;
+    PropertyName*        moduleFunctionName_;
+
+    UniqueChars          errorString_;
+    uint32_t             errorOffset_;
+    bool                 errorOverRecursed_;
+
+    bool                 canValidateChangeHeap_;
+    bool                 hasChangeHeap_;
+    bool                 supportsSimd_;
+    bool                 atomicsPresent_;
 
   public:
     ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
         mg_(cx),
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
         functions_(cx),
         funcPtrTables_(cx),
         globals_(cx),
         arrayViews_(cx),
-        exits_(cx),
+        imports_(cx),
         standardLibraryMathNames_(cx),
         standardLibraryAtomicsNames_(cx),
         standardLibrarySimdOpNames_(cx),
         moduleFunctionNode_(parser.pc->maybeFunction),
         moduleFunctionName_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
@@ -1298,18 +2001,18 @@ class MOZ_STACK_CLASS ModuleValidator
         JSAtom* atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
     }
 
   public:
 
-    bool init() {
-        if (!globals_.init() || !exits_.init())
+    bool init(HandleAsmJSModule moduleObj) {
+        if (!globals_.init() || !imports_.init())
             return false;
 
         if (!standardLibraryMathNames_.init() ||
             !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
             !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
             !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
             !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
             !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
@@ -1366,183 +2069,212 @@ class MOZ_STACK_CLASS ModuleValidator
         uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
 
         // "use strict" should be added to the source if we are in an implicit
         // strict context, see also comment above addUseStrict in
         // js::FunctionToString.
         bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
 
-        return mg_.init(parser_.ss, srcStart, srcBodyStart, strict);
-    }
-
-    bool finish(ScopedJSDeletePtr<AsmJSModule>* module, SlowFunctionVector* slowFuncs) {
-        return mg_.finish(parser_.tokenStream, module, slowFuncs);
+        module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict);
+        if (!module_)
+            return false;
+
+        moduleObj->setModule(module_);
+
+        return mg_.init();
+    }
+
+    bool finish(SlowFunctionVector* slowFuncs) {
+        uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
+        TokenPos pos;
+        JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
+        uint32_t endAfterCurly = pos.end;
+
+        auto usesHeap = Module::HeapBool(module_->hasArrayView());
+        auto sharedHeap = Module::SharedBool(module_->isSharedView());
+        auto mutedErrors = Module::MutedBool(parser_.ss->mutedErrors());
+
+        CacheableChars filename = make_string_copy(parser_.ss->filename());
+        if (!filename)
+            return false;
+
+        CacheableTwoByteChars displayURL;
+        if (parser_.ss->hasDisplayURL()) {
+            uint32_t length = js_strlen(parser_.ss->displayURL());
+            displayURL.reset(js_pod_calloc<char16_t>(length + 1));
+            if (!displayURL)
+                return false;
+            PodCopy(displayURL.get(), parser_.ss->displayURL(), length);
+        }
+
+        UniqueStaticLinkData linkData;
+        Module* wasm = mg_.finish(usesHeap, sharedHeap, mutedErrors,
+                                  Move(filename), Move(displayURL),
+                                  &linkData, slowFuncs);
+        if (!wasm)
+            return false;
+
+        module_->finish(wasm, Move(linkData), endBeforeCurly, endAfterCurly);
+        return true;
     }
 
     // Mutable interface.
     void initModuleFunctionName(PropertyName* name) { moduleFunctionName_ = name; }
     void initGlobalArgumentName(PropertyName* n)    { module().initGlobalArgumentName(n); }
     void initImportArgumentName(PropertyName* n)    { module().initImportArgumentName(n); }
     void initBufferArgumentName(PropertyName* n)    { module().initBufferArgumentName(n); }
 
-    bool addGlobalVarInit(PropertyName* varName, const NumLit& lit, bool isConst) {
-        // The type of a const is the exact type of the literal (since its value
-        // cannot change) which is more precise than the corresponding vartype.
-        Type type = isConst ? Type::lit(lit) : Type::var(lit.type());
+    bool addGlobalVarInit(PropertyName* var, const NumLit& lit, bool isConst) {
         uint32_t globalDataOffset;
-        if (!module().addGlobalVarInit(lit.value(), &globalDataOffset))
+        if (!mg_.allocateGlobalVar(lit.type(), &globalDataOffset))
             return false;
         Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.globalDataOffset_ = globalDataOffset;
-        global->u.varOrConst.type_ = type.which();
+        global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : Type::var(lit.type())).which();
         if (isConst)
             global->u.varOrConst.literalValue_ = lit;
-        return globals_.putNew(varName, global);
-    }
-    bool addGlobalVarImport(PropertyName* varName, PropertyName* fieldName, ValType importType,
-                            bool isConst)
-    {
+        return globals_.putNew(var, global) &&
+               module().addGlobalVarInit(lit.value(), globalDataOffset);
+    }
+    bool addGlobalVarImport(PropertyName* var, PropertyName* field, ValType type, bool isConst) {
         uint32_t globalDataOffset;
-        if (!module().addGlobalVarImport(fieldName, importType, &globalDataOffset))
+        if (!mg_.allocateGlobalVar(type, &globalDataOffset))
             return false;
         Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
         Global* global = validationLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.globalDataOffset_ = globalDataOffset;
-        global->u.varOrConst.type_ = Type::var(importType).which();
-        return globals_.putNew(varName, global);
-    }
-    bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField)
-    {
-        if (!arrayViews_.append(ArrayView(varName, vt)))
+        global->u.varOrConst.type_ = Type::var(type).which();
+        return globals_.putNew(var, global) &&
+               module().addGlobalVarImport(field, type, globalDataOffset);
+    }
+    bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
+        if (!arrayViews_.append(ArrayView(var, vt)))
             return false;
         Global* global = validationLifo_.new_<Global>(Global::ArrayView);
         if (!global)
             return false;
-        if (!module().addArrayView(vt, maybeField))
-            return false;
         global->u.viewInfo.viewType_ = vt;
-        return globals_.putNew(varName, global);
-    }
-    bool addMathBuiltinFunction(PropertyName* varName, AsmJSMathBuiltinFunction func,
-                                PropertyName* fieldName)
+        return globals_.putNew(var, global) &&
+               module().addArrayView(vt, maybeField);
+    }
+    bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func,
+                                PropertyName* field)
     {
-        if (!module().addMathBuiltinFunction(func, fieldName))
-            return false;
         Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
         if (!global)
             return false;
         global->u.mathBuiltinFunc_ = func;
-        return globals_.putNew(varName, global);
+        return globals_.putNew(var, global) &&
+               module().addMathBuiltinFunction(func, field);
     }
   private:
-    bool addGlobalDoubleConstant(PropertyName* varName, double constant) {
+    bool addGlobalDoubleConstant(PropertyName* var, double constant) {
         Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
         if (!global)
             return false;
         global->u.varOrConst.type_ = Type::Double;
         global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant));
-        return globals_.putNew(varName, global);
+        return globals_.putNew(var, global);
     }
   public:
-    bool addMathBuiltinConstant(PropertyName* varName, double constant, PropertyName* fieldName) {
-        if (!module().addMathBuiltinConstant(constant, fieldName))
-            return false;
-        return addGlobalDoubleConstant(varName, constant);
-    }
-    bool addGlobalConstant(PropertyName* varName, double constant, PropertyName* fieldName) {
-        if (!module().addGlobalConstant(constant, fieldName))
-            return false;
-        return addGlobalDoubleConstant(varName, constant);
-    }
-    bool addAtomicsBuiltinFunction(PropertyName* varName, AsmJSAtomicsBuiltinFunction func,
-                                   PropertyName* fieldName)
+    bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) {
+        return addGlobalDoubleConstant(var, constant) &&
+               module().addMathBuiltinConstant(constant, field);
+    }
+    bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) {
+        return addGlobalDoubleConstant(var, constant) &&
+               module().addGlobalConstant(constant, field);
+    }
+    bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func,
+                                   PropertyName* field)
     {
-        if (!module().addAtomicsBuiltinFunction(func, fieldName))
-            return false;
         Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
         if (!global)
             return false;
         atomicsPresent_ = true;
         global->u.atomicsBuiltinFunc_ = func;
-        return globals_.putNew(varName, global);
-    }
-    bool addSimdCtor(PropertyName* varName, AsmJSSimdType type, PropertyName* fieldName) {
-        if (!module().addSimdCtor(type, fieldName))
-            return false;
+        return globals_.putNew(var, global) &&
+               module().addAtomicsBuiltinFunction(func, field);
+    }
+    bool addSimdCtor(PropertyName* var, AsmJSSimdType type, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::SimdCtor);
         if (!global)
             return false;
         global->u.simdCtorType_ = type;
-        return globals_.putNew(varName, global);
-    }
-    bool addSimdOperation(PropertyName* varName, AsmJSSimdType type, AsmJSSimdOperation op,
-                          PropertyName* typeVarName, PropertyName* opName)
+        return globals_.putNew(var, global) &&
+               module().addSimdCtor(type, field);
+    }
+    bool addSimdOperation(PropertyName* var, AsmJSSimdType type, AsmJSSimdOperation op,
+                          PropertyName* opName)
     {
-        if (!module().addSimdOperation(type, op, opName))
-            return false;
         Global* global = validationLifo_.new_<Global>(Global::SimdOperation);
         if (!global)
             return false;
         global->u.simdOp.type_ = type;
         global->u.simdOp.which_ = op;
-        return globals_.putNew(varName, global);
+        return globals_.putNew(var, global) &&
+               module().addSimdOperation(type, op, opName);
     }
     bool addByteLength(PropertyName* name) {
         canValidateChangeHeap_ = true;
-        if (!module().addByteLength())
-            return false;
         Global* global = validationLifo_.new_<Global>(Global::ByteLength);
-        return global && globals_.putNew(name, global);
+        return global &&
+               globals_.putNew(name, global) &&
+               module().addByteLength();
     }
     bool addChangeHeap(PropertyName* name, ParseNode* fn, uint32_t mask, uint32_t min, uint32_t max) {
         hasChangeHeap_ = true;
-        module().addChangeHeap(mask, min, max);
         Global* global = validationLifo_.new_<Global>(Global::ChangeHeap);
         if (!global)
             return false;
         global->u.changeHeap.srcBegin_ = fn->pn_pos.begin;
         global->u.changeHeap.srcEnd_ = fn->pn_pos.end;
-        return globals_.putNew(name, global);
-    }
-    bool addArrayViewCtor(PropertyName* varName, Scalar::Type vt, PropertyName* fieldName) {
+        return globals_.putNew(name, global) &&
+               module().addChangeHeap(mask, min, max);
+    }
+    bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
         if (!global)
             return false;
-        if (!module().addArrayViewCtor(vt, fieldName))
-            return false;
         global->u.viewInfo.viewType_ = vt;
-        return globals_.putNew(varName, global);
-    }
-    bool addFFI(PropertyName* varName, PropertyName* field) {
+        return globals_.putNew(var, global) &&
+               module().addArrayViewCtor(vt, field);
+    }
+    bool addFFI(PropertyName* var, PropertyName* field) {
         Global* global = validationLifo_.new_<Global>(Global::FFI);
         if (!global)
             return false;
         uint32_t index;
         if (!module().addFFI(field, &index))
             return false;
         global->u.ffiIndex_ = index;
-        return globals_.putNew(varName, global);
-    }
-    bool addExportedFunction(const Func& func, PropertyName* maybeFieldName) {
+        return globals_.putNew(var, global);
+    }
+    bool addExport(ParseNode* pn, const Func& func, PropertyName* maybeFieldName) {
         MallocSig::ArgVector args;
         if (!args.appendAll(func.sig().args()))
             return false;
         MallocSig sig(Move(args), func.sig().ret());
-        return module().addExportedFunction(func.name(), func.index(), func.srcBegin(),
-                                            func.srcEnd(), maybeFieldName, Move(sig));
-    }
-    bool addExportedChangeHeap(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
-        return module().addExportedChangeHeap(name, g.changeHeapSrcBegin(), g.changeHeapSrcEnd(),
-                                              maybeFieldName);
+        uint32_t wasmIndex;
+        if (!mg_.declareExport(Move(sig), func.index(), &wasmIndex))
+            return false;
+        if (wasmIndex == AsmJSModule::Export::ChangeHeap)
+            return fail(pn, "too many exports");
+        return module().addExport(func.name(), maybeFieldName, wasmIndex,
+                                  func.srcBegin(), func.srcEnd());
+    }
+    bool addChangeHeapExport(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
+        return module().addExport(name, maybeFieldName, AsmJSModule::Export::ChangeHeap,
+                                  g.changeHeapSrcBegin(), g.changeHeapSrcEnd());
     }
   private:
     const LifoSig* getLifoSig(const LifoSig& sig) {
         return &sig;
     }
     const LifoSig* getLifoSig(const MallocSig& sig) {
         return mg_.newLifoSig(sig);
     }
@@ -1554,19 +2286,17 @@ class MOZ_STACK_CLASS ModuleValidator
             return false;
         global->u.funcIndex_ = funcIndex;
         if (!globals_.putNew(name, global))
             return false;
         const LifoSig* lifoSig = getLifoSig(sig);
         if (!lifoSig)
             return false;
         *func = validationLifo_.new_<Func>(name, firstUse, *lifoSig, funcIndex);
-        if (!*func)
-            return false;
-        return functions_.append(*func);
+        return *func && functions_.append(*func);
     }
     template <class SigT>
     bool declareFuncPtrTable(PropertyName* name, uint32_t firstUse, SigT& sig, uint32_t mask,
                              uint32_t* index)
     {
         if (!mg_.declareFuncPtrTable(/* numElems = */ mask + 1, index))
             return false;
         MOZ_ASSERT(*index == numFuncPtrTables());
@@ -1577,39 +2307,40 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!globals_.putNew(name, global))
             return false;
         const LifoSig* lifoSig = getLifoSig(sig);
         if (!lifoSig)
             return false;
         FuncPtrTable* t = validationLifo_.new_<FuncPtrTable>(cx_, name, firstUse, *lifoSig, mask);
         return t && funcPtrTables_.append(t);
     }
-    bool defineFuncPtrTable(uint32_t funcPtrTableIndex, ModuleGenerator::FuncIndexVector&& elems) {
+    bool defineFuncPtrTable(uint32_t funcPtrTableIndex, const Vector<uint32_t>& elems) {
         FuncPtrTable& table = *funcPtrTables_[funcPtrTableIndex];
         if (table.defined())
             return false;
         table.define();
-        return mg_.defineFuncPtrTable(funcPtrTableIndex, Move(elems));
-    }
-    bool addExit(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* exitIndex,
+        mg_.defineFuncPtrTable(funcPtrTableIndex, elems);
+        return true;
+    }
+    bool addImport(PropertyName* name, MallocSig&& sig, unsigned ffiIndex, unsigned* importIndex,
                  const LifoSig** lifoSig)
     {
-        ExitDescriptor::Lookup lookup(name, sig);
-        ExitMap::AddPtr p = exits_.lookupForAdd(lookup);
+        ImportDescriptor::Lookup lookup(name, sig);
+        ImportMap::AddPtr p = imports_.lookupForAdd(lookup);
         if (p) {
             *lifoSig = &p->key().sig();
-            *exitIndex = p->value();
+            *importIndex = p->value();
             return true;
         }
         *lifoSig = getLifoSig(sig);
         if (!*lifoSig)
             return false;
-        if (!module().addExit(Move(sig), ffiIndex, exitIndex))
-            return false;
-        return exits_.add(p, ExitDescriptor(name, **lifoSig), *exitIndex);
+        return mg_.declareImport(Move(sig), importIndex) &&
+               imports_.add(p, ImportDescriptor(name, **lifoSig), *importIndex) &&
+               module().addImport(ffiIndex, *importIndex);
     }
 
     bool tryOnceToValidateChangeHeap() {
         bool ret = canValidateChangeHeap_;
         canValidateChangeHeap_ = false;
         return ret;
     }
     bool hasChangeHeap() const {
@@ -1631,17 +2362,17 @@ class MOZ_STACK_CLASS ModuleValidator
         return !!errorString_;
     }
 
     bool failOffset(uint32_t offset, const char* str) {
         MOZ_ASSERT(!hasAlreadyFailed());
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(str);
         errorOffset_ = offset;
-        errorString_ = DuplicateString(cx_, str);
+        errorString_ = make_string_copy(str);
         return false;
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return failOffset(pn->pn_pos.begin, str);
     }
 
     bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) {
@@ -1687,17 +2418,17 @@ class MOZ_STACK_CLASS ModuleValidator
         return false;
     }
 
     // Read-only interface
     ExclusiveContext* cx() const             { return cx_; }
     ParseNode* moduleFunctionNode() const    { return moduleFunctionNode_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
     ModuleGenerator& mg()                    { return mg_; }
-    AsmJSModule& module() const              { return mg_.module(); }
+    AsmJSModule& module() const              { return *module_; }
     AsmJSParser& parser() const              { return parser_; }
     TokenStream& tokenStream() const         { return parser_.tokenStream; }
     bool supportsSimd() const                { return supportsSimd_; }
 
     unsigned numArrayViews() const {
         return arrayViews_.length();
     }
     const ArrayView& arrayView(unsigned i) const {
@@ -1748,19 +2479,29 @@ class MOZ_STACK_CLASS ModuleValidator
     bool lookupStandardSimdOpName(PropertyName* name, AsmJSSimdOperation* op) const {
         if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) {
             *op = p->value();
             return true;
         }
         return false;
     }
 
-    void startFunctionBodies() {
-        if (atomicsPresent_)
+    bool startFunctionBodies() {
+        if (atomicsPresent_) {
+#if defined(ENABLE_SHARED_ARRAY_BUFFER)
             module().setViewsAreShared();
+#else
+            return failOffset(parser_.tokenStream.currentToken().pos.begin,
+                              "shared memory and atomics not supported by this build");
+#endif
+        }
+        return true;
+    }
+    bool finishFunctionBodies() {
+        return mg_.finishFuncs();
     }
 };
 
 } // namespace
 
 /*****************************************************************************/
 // Numeric literal utilities
 
@@ -2675,26 +3416,25 @@ CheckGlobalSimdImport(ModuleValidator& m
     AsmJSSimdType simdType;
     if (!IsSimdTypeName(m, field, &simdType))
         return m.failName(initNode, "'%s' is not a standard SIMD type", field);
     return m.addSimdCtor(varName, simdType, field);
 }
 
 static bool
 CheckGlobalSimdOperationImport(ModuleValidator& m, const ModuleValidator::Global* global,
-                               ParseNode* initNode, PropertyName* varName, PropertyName* ctorVarName,
-                               PropertyName* opName)
+                               ParseNode* initNode, PropertyName* varName, PropertyName* opName)
 {
     AsmJSSimdType simdType = global->simdCtorType();
     AsmJSSimdOperation simdOp;
     if (!m.lookupStandardSimdOpName(opName, &simdOp))
         return m.failName(initNode, "'%s' is not a standard SIMD operation", opName);
     if (!IsSimdValidOperationType(simdType, simdOp))
         return m.failName(initNode, "'%s' is not an operation supported by the SIMD type", opName);
-    return m.addSimdOperation(varName, simdType, simdOp, ctorVarName, opName);
+    return m.addSimdOperation(varName, simdType, simdOp, opName);
 }
 
 static bool
 CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode)
 {
     ParseNode* base = DotBase(initNode);
     PropertyName* field = DotMember(initNode);
 
@@ -2746,17 +3486,17 @@ CheckGlobalDotImport(ModuleValidator& m,
 
     const ModuleValidator::Global* global = m.lookupGlobal(base->name());
     if (!global)
         return m.failName(initNode, "%s not found in module global scope", base->name());
 
     if (!global->isSimdCtor())
         return m.failName(base, "expecting SIMD constructor name, got %s", field);
 
-    return CheckGlobalSimdOperationImport(m, global, initNode, varName, base->name(), field);
+    return CheckGlobalSimdOperationImport(m, global, initNode, varName, field);
 }
 
 static bool
 CheckModuleGlobal(ModuleValidator& m, ParseNode* var, bool isConst)
 {
     if (!IsDefinition(var))
         return m.fail(var, "import variable names must be unique");
 
@@ -3667,17 +4407,17 @@ CheckAtomicsStore(FunctionValidator& f, 
     f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
     f.patchU8(viewTypeAt, uint8_t(viewType));
 
     *type = rhsType;
     return true;
 }
 
 static bool
-CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, js::jit::AtomicOp op)
+CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op)
 {
     if (CallArgListLength(call) != 3)
         return f.fail(call, "Atomics binary operator must be passed 3 arguments");
 
     ParseNode* arrayArg = CallArgList(call);
     ParseNode* indexArg = NextNode(arrayArg);
     ParseNode* valueArg = NextNode(indexArg);
 
@@ -4049,18 +4789,17 @@ CheckFuncPtrCall(FunctionValidator& f, P
         return false;
 
     MallocSig sig(Move(args), ret);
 
     uint32_t funcPtrTableIndex;
     if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, sig, mask, &funcPtrTableIndex))
         return false;
 
-    uint32_t globalDataOffset = f.m().module().funcPtrTable(funcPtrTableIndex).globalDataOffset();
-    f.patch32(globalDataOffsetAt, globalDataOffset);
+    f.patch32(globalDataOffsetAt, f.m().mg().funcPtrTableGlobalDataOffset(funcPtrTableIndex));
     f.patchSig(sigAt, &f.m().funcPtrTable(funcPtrTableIndex).sig());
 
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckIsExternType(FunctionValidator& f, ParseNode* argNode, Type type)
@@ -4094,34 +4833,33 @@ CheckFFICall(FunctionValidator& f, Parse
       case ExprType::F64:    f.writeOp(F64::CallImport);   break;
       case ExprType::I32x4:  f.writeOp(I32X4::CallImport); break;
       case ExprType::F32x4:  f.writeOp(F32X4::CallImport); break;
       case ExprType::B32x4:  f.writeOp(B32X4::CallImport); break;
     }
 
     // Global data offset
     size_t offsetAt = f.temp32();
-    // Pointer to the exit's signature in the module's lifo
+    // Pointer to the import's signature in the module's lifo
     size_t sigAt = f.tempPtr();
     // Call node position (asm.js specific)
     WriteCallLineCol(f, callNode);
 
     MallocSig::ArgVector args;
     if (!CheckCallArgs<CheckIsExternType>(f, callNode, &args))
         return false;
 
     MallocSig sig(Move(args), ret);
 
-    unsigned exitIndex = 0;
+    unsigned importIndex = 0;
     const LifoSig* lifoSig = nullptr;
-    if (!f.m().addExit(calleeName, Move(sig), ffiIndex, &exitIndex, &lifoSig))
-        return false;
-
-    JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0);
-    f.patch32(offsetAt, f.module().exit(exitIndex).globalDataOffset());
+    if (!f.m().addImport(calleeName, Move(sig), ffiIndex, &importIndex, &lifoSig))
+        return false;
+
+    f.patch32(offsetAt, f.m().mg().importExitGlobalDataOffset(importIndex));
     f.patchSig(sigAt, lifoSig);
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType,
                       size_t opcodeAt)
@@ -5851,17 +6589,17 @@ CheckExprStatement(FunctionValidator& f,
 enum class InterruptCheckPosition {
     Head,
     Loop
 };
 
 static void
 MaybeAddInterruptCheck(FunctionValidator& f, InterruptCheckPosition pos, ParseNode* pn)
 {
-    if (f.m().module().usesSignalHandlersForInterrupt())
+    if (f.m().mg().args().useSignalHandlersForInterrupt)
         return;
 
     switch (pos) {
       case InterruptCheckPosition::Head: f.writeOp(Stmt::InterruptCheckHead); break;
       case InterruptCheckPosition::Loop: f.writeOp(Stmt::InterruptCheckLoop); break;
     }
 
     unsigned lineno = 0, column = 0;
@@ -6680,17 +7418,17 @@ CheckFuncPtrTable(ModuleValidator& m, Pa
 
     unsigned length = ListLength(arrayLiteral);
 
     if (!IsPowerOfTwo(length))
         return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
 
     unsigned mask = length - 1;
 
-    ModuleGenerator::FuncIndexVector elems;
+    Vector<uint32_t> elemFuncIndices(m.cx());
     const LifoSig* sig = nullptr;
     for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
         if (!elem->isKind(PNK_NAME))
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         PropertyName* funcName = elem->name();
         const ModuleValidator::Func* func = m.lookupFunction(funcName);
         if (!func)
@@ -6698,25 +7436,25 @@ CheckFuncPtrTable(ModuleValidator& m, Pa
 
         if (sig) {
             if (*sig != func->sig())
                 return m.fail(elem, "all functions in table must have same signature");
         } else {
             sig = &func->sig();
         }
 
-        if (!elems.append(func->index()))
+        if (!elemFuncIndices.append(func->index()))
             return false;
     }
 
     uint32_t funcPtrTableIndex;
     if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), *sig, mask, &funcPtrTableIndex))
         return false;
 
-    if (!m.defineFuncPtrTable(funcPtrTableIndex, Move(elems)))
+    if (!m.defineFuncPtrTable(funcPtrTableIndex, elemFuncIndices))
         return m.fail(var, "duplicate function-pointer definition");
 
     return true;
 }
 
 static bool
 CheckFuncPtrTables(ModuleValidator& m)
 {
@@ -6751,20 +7489,20 @@ CheckModuleExportFunction(ModuleValidato
         return m.fail(pn, "expected name of exported function");
 
     PropertyName* funcName = pn->name();
     const ModuleValidator::Global* global = m.lookupGlobal(funcName);
     if (!global)
         return m.failName(pn, "exported function name '%s' not found", funcName);
 
     if (global->which() == ModuleValidator::Global::Function)
-        return m.addExportedFunction(m.function(global->funcIndex()), maybeFieldName);
+        return m.addExport(pn, m.function(global->funcIndex()), maybeFieldName);
 
     if (global->which() == ModuleValidator::Global::ChangeHeap)
-        return m.addExportedChangeHeap(funcName, *global, maybeFieldName);
+        return m.addChangeHeapExport(funcName, *global, maybeFieldName);
 
     return m.failName(pn, "'%s' is not a function", funcName);
 }
 
 static bool
 CheckModuleExportObject(ModuleValidator& m, ParseNode* object)
 {
     MOZ_ASSERT(object->isKind(PNK_OBJECT));
@@ -6837,24 +7575,23 @@ CheckModuleEnd(ModuleValidator &m)
                             "top-level export (return) must be the last statement");
     }
 
     m.parser().tokenStream.ungetToken();
     return true;
 }
 
 static bool
-CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList,
-            ScopedJSDeletePtr<AsmJSModule>* module, unsigned* time,
-            SlowFunctionVector* slowFuncs)
+CheckModule(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, HandleAsmJSModule obj,
+            unsigned* time, SlowFunctionVector* slowFuncs)
 {
     int64_t before = PRMJ_Now();
 
     ModuleValidator m(cx, parser);
-    if (!m.init())
+    if (!m.init(obj))
         return false;
 
     if (PropertyName* moduleFunctionName = FunctionName(m.moduleFunctionNode())) {
         if (!CheckModuleLevelName(m, m.moduleFunctionNode(), moduleFunctionName))
             return false;
         m.initModuleFunctionName(moduleFunctionName);
     }
 
@@ -6868,109 +7605,1306 @@ CheckModule(ExclusiveContext* cx, AsmJSP
         return false;
 
     if (!CheckModuleProcessingDirectives(m))
         return false;
 
     if (!CheckModuleGlobals(m))
         return false;
 
-    m.startFunctionBodies();
-
-#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
-    if (m.usesSharedMemory())
-        return m.failOffset(m.parser().tokenStream.currentToken().pos.begin,
-                            "shared memory and atomics not supported by this build");
-#endif
+    if (!m.startFunctionBodies())
+        return false;
 
     if (!CheckFunctions(m))
         return false;
 
+    if (!m.finishFunctionBodies())
+        return false;
+
     if (!CheckFuncPtrTables(m))
         return false;
 
     if (!CheckModuleReturn(m))
         return false;
 
     if (!CheckModuleEnd(m))
         return false;
 
-    if (!m.finish(module, slowFuncs))
+    if (!m.finish(slowFuncs))
         return false;
 
     *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
     return true;
 }
 
-static bool
-BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module,
-                    unsigned time, const SlowFunctionVector& slowFuncs,
-                    JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out)
-{
-#ifndef JS_MORE_DETERMINISTIC
-    ScopedJSFreePtr<char> slowText;
-    if (!slowFuncs.empty()) {
-        slowText.reset(JS_smprintf("; %d functions compiled slowly: ", slowFuncs.length()));
-        if (!slowText)
-            return true;
-
-        for (unsigned i = 0; i < slowFuncs.length(); i++) {
-            const SlowFunction& func = slowFuncs[i];
-            JSAutoByteString name;
-            if (!AtomToPrintableString(cx, func.name, &name))
+/*****************************************************************************/
+// Runtime calls to asm.js module exports
+
+static AsmJSModuleObject&
+FunctionToModuleObject(JSFunction* fun)
+{
+    MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun));
+    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT);
+    return v.toObject().as<AsmJSModuleObject>();
+}
+
+static unsigned
+FunctionToExportIndex(JSFunction* fun)
+{
+    MOZ_ASSERT(IsAsmJSFunction(fun));
+    const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT);
+    return v.toInt32();
+}
+
+static bool
+ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
+{
+    HandleValue bufferArg = args.get(0);
+    if (!IsArrayBuffer(bufferArg)) {
+        ReportIncompatible(cx, args);
+        return false;
+    }
+
+    Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
+    uint32_t heapLength = newBuffer->byteLength();
+    if (heapLength & module.heapLengthMask() ||
+        heapLength < module.minHeapLength() ||
+        heapLength > module.maxHeapLength())
+    {
+        args.rval().set(BooleanValue(false));
+        return true;
+    }
+
+    if (!module.hasArrayView()) {
+        args.rval().set(BooleanValue(true));
+        return true;
+    }
+
+    MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
+
+    bool useSignalHandlers = module.wasmModule().compileArgs().useSignalHandlersForOOB;
+    if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
+        return false;
+
+    args.rval().set(BooleanValue(module.wasmModule().changeHeap(newBuffer, cx)));
+    return true;
+}
+
+static bool
+CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedFunction callee(cx, &args.callee().as<JSFunction>());
+
+    AsmJSModule& module = FunctionToModuleObject(callee).module();
+    const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)];
+
+    // The heap-changing function is a special-case and is implemented by C++.
+    if (exp.isChangeHeap())
+        return ChangeHeap(cx, module, args);
+
+    return module.wasmModule().callExport(cx, exp.wasmIndex(), args);
+}
+
+/*****************************************************************************/
+// Link-time validation
+
+static bool
+LinkFail(JSContext* cx, const char* str)
+{
+    JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
+                                 nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
+    return false;
+}
+
+static bool
+GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
+{
+    if (!objVal.isObject())
+        return LinkFail(cx, "accessing property of non-object");
+
+    RootedObject obj(cx, &objVal.toObject());
+    if (IsScriptedProxy(obj))
+        return LinkFail(cx, "accessing property of a Proxy");
+
+    Rooted<PropertyDescriptor> desc(cx);
+    RootedId id(cx, NameToId(field));
+    if (!GetPropertyDescriptor(cx, obj, id, &desc))
+        return false;
+
+    if (!desc.object())
+        return LinkFail(cx, "property not present on object");
+
+    if (!desc.isDataDescriptor())
+        return LinkFail(cx, "property is not a data property");
+
+    v.set(desc.value());
+    return true;
+}
+
+static bool
+HasPureCoercion(JSContext* cx, HandleValue v)
+{
+    if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v) || IsVectorObject<Bool32x4>(v))
+        return true;
+
+    // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
+    // bug that generates code that passes functions for some imports. To avoid
+    // breaking all the code that contains this bug, we make an exception for
+    // functions that don't have user-defined valueOf or toString, for their
+    // coercions are not observable and coercion via ToNumber/ToInt32
+    // definitely produces NaN/0. We should remove this special case later once
+    // most apps have been built with newer Emscripten.
+    jsid toString = NameToId(cx->names().toString);
+    if (v.toObject().is<JSFunction>() &&
+        HasObjectValueOf(&v.toObject(), cx) &&
+        ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
+    {
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData,
+                       HandleValue importVal)
+{
+    void* datum = globalData + global.varGlobalDataOffset();
+
+    switch (global.varInitKind()) {
+      case AsmJSModule::Global::InitConstant: {
+        Val v = global.varInitVal();
+        switch (v.type()) {
+          case ValType::I32:
+            *(int32_t*)datum = v.i32();
+            break;
+          case ValType::I64:
+            MOZ_CRASH("int64");
+          case ValType::F32:
+            *(float*)datum = v.f32();
+            break;
+          case ValType::F64:
+            *(double*)datum = v.f64();
+            break;
+          case ValType::I32x4:
+          case ValType::B32x4:
+            // Bool32x4 uses the same data layout as Int32x4.
+            memcpy(datum, v.i32x4(), Simd128DataSize);
+            break;
+          case ValType::F32x4:
+            memcpy(datum, v.f32x4(), Simd128DataSize);
+            break;
+        }
+        break;
+      }
+
+      case AsmJSModule::Global::InitImport: {
+        RootedPropertyName field(cx, global.varImportField());
+        RootedValue v(cx);
+        if (!GetDataProperty(cx, importVal, field, &v))
+            return false;
+
+        if (!v.isPrimitive() && !HasPureCoercion(cx, v))
+            return LinkFail(cx, "Imported values must be primitives");
+
+        switch (global.varInitImportType()) {
+          case ValType::I32:
+            if (!ToInt32(cx, v, (int32_t*)datum))
+                return false;
+            break;
+          case ValType::I64:
+            MOZ_CRASH("int64");
+          case ValType::F32:
+            if (!RoundFloat32(cx, v, (float*)datum))
+                return false;
+            break;
+          case ValType::F64:
+            if (!ToNumber(cx, v, (double*)datum))
+                return false;
+            break;
+          case ValType::I32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
+                return false;
+            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+            break;
+          }
+          case ValType::F32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
+                return false;
+            memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
+            break;
+          }
+          case ValType::B32x4: {
+            SimdConstant simdConstant;
+            if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
                 return false;
-
-            slowText.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowText.get(),
-                                       name.ptr(), func.line, func.column, func.ms,
-                                       i+1 < slowFuncs.length() ? ", " : ""));
-            if (!slowText)
-                return true;
-        }
-    }
-
-    const char* cacheString = "";
-    switch (cacheResult) {
-      case JS::AsmJSCache_Success:
-        cacheString = "stored in cache";
+            // Bool32x4 uses the same data layout as Int32x4.
+            memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+            break;
+          }
+        }
+        break;
+      }
+    }
+
+    return true;
+}
+
+static bool
+ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal,
+            AutoVectorRooter<JSFunction*>* ffis)
+{
+    RootedPropertyName field(cx, global.ffiField());
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, importVal, field, &v))
+        return false;
+
+    if (!v.isObject() || !v.toObject().is<JSFunction>())
+        return LinkFail(cx, "FFI imports must be functions");
+
+    (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
+    return true;
+}
+
+static bool
+ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedPropertyName field(cx, global.maybeViewName());
+    if (!field)
+        return true;
+
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, field, &v))
+        return false;
+
+    bool tac = IsTypedArrayConstructor(v, global.viewType());
+    if (!tac)
+        return LinkFail(cx, "bad typed array constructor");
+
+    return true;
+}
+
+static bool
+ValidateByteLength(JSContext* cx, HandleValue globalVal)
+{
+    RootedPropertyName field(cx, cx->names().byteLength);
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, field, &v))
+        return false;
+
+    if (!v.isObject() || !v.toObject().isBoundFunction())
+        return LinkFail(cx, "byteLength must be a bound function object");
+
+    RootedFunction fun(cx, &v.toObject().as<JSFunction>());
+
+    RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
+    if (!IsNativeFunction(boundTarget, fun_call))
+        return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
+
+    RootedValue boundThis(cx, fun->getBoundFunctionThis());
+    if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
+        return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
+
+    return true;
+}
+
+static bool
+ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
+        return false;
+
+    RootedPropertyName field(cx, global.mathName());
+    if (!GetDataProperty(cx, v, field, &v))
+        return false;
+
+    Native native = nullptr;
+    switch (global.mathBuiltinFunction()) {
+      case AsmJSMathBuiltin_sin: native = math_sin; break;
+      case AsmJSMathBuiltin_cos: native = math_cos; break;
+      case AsmJSMathBuiltin_tan: native = math_tan; break;
+      case AsmJSMathBuiltin_asin: native = math_asin; break;
+      case AsmJSMathBuiltin_acos: native = math_acos; break;
+      case AsmJSMathBuiltin_atan: native = math_atan; break;
+      case AsmJSMathBuiltin_ceil: native = math_ceil; break;
+      case AsmJSMathBuiltin_floor: native = math_floor; break;
+      case AsmJSMathBuiltin_exp: native = math_exp; break;
+      case AsmJSMathBuiltin_log: native = math_log; break;
+      case AsmJSMathBuiltin_pow: native = math_pow; break;
+      case AsmJSMathBuiltin_sqrt: native = math_sqrt; break;
+      case AsmJSMathBuiltin_min: native = math_min; break;
+      case AsmJSMathBuiltin_max: native = math_max; break;
+      case AsmJSMathBuiltin_abs: native = math_abs; break;
+      case AsmJSMathBuiltin_atan2: native = math_atan2; break;
+      case AsmJSMathBuiltin_imul: native = math_imul; break;
+      case AsmJSMathBuiltin_clz32: native = math_clz32; break;
+      case AsmJSMathBuiltin_fround: native = math_fround; break;
+    }
+
+    if (!IsNativeFunction(v, native))
+        return LinkFail(cx, "bad Math.* builtin function");
+
+    return true;
+}
+
+static PropertyName*
+SimdTypeToName(JSContext* cx, AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4:   return cx->names().int32x4;
+      case AsmJSSimdType_float32x4: return cx->names().float32x4;
+      case AsmJSSimdType_bool32x4:  return cx->names().bool32x4;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
+}
+
+static SimdTypeDescr::Type
+AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
+{
+    switch (type) {
+      case AsmJSSimdType_int32x4: return Int32x4::type;
+      case AsmJSSimdType_float32x4: return Float32x4::type;
+      case AsmJSSimdType_bool32x4: return Bool32x4::type;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
+}
+
+static bool
+ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal,
+                 MutableHandleValue out)
+{
+    RootedValue v(cx);
+    if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
+        return false;
+
+    AsmJSSimdType type;
+    if (global.which() == AsmJSModule::Global::SimdCtor)
+        type = global.simdCtorType();
+    else
+        type = global.simdOperationType();
+
+    RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
+    if (!GetDataProperty(cx, v, simdTypeName, &v))
+        return false;
+
+    if (!v.isObject())
+        return LinkFail(cx, "bad SIMD type");
+
+    RootedObject simdDesc(cx, &v.toObject());
+    if (!simdDesc->is<SimdTypeDescr>())
+        return LinkFail(cx, "bad SIMD type");
+
+    if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type())
+        return LinkFail(cx, "bad SIMD type");
+
+    out.set(v);
+    return true;
+}
+
+static bool
+ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    RootedValue _(cx);
+    return ValidateSimdType(cx, global, globalVal, &_);
+}
+
+static bool
+ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+    // SIMD operations are loaded from the SIMD type, so the type must have been
+    // validated before the operation.
+    RootedValue v(cx);
+    JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
+
+    RootedPropertyName opName(cx, global.simdOperationName());
+    if (!GetDataProperty(cx, v, opName, &v))
+        return false;
+
+    Native native = nullptr;
+    switch (global.simdOperationType()) {
+#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break;
+#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break;
+#define SET_NATIVE_BOOL32X4(op) case AsmJSSimdOperation_##op: native = simd_bool32x4_##op; break;
+#define FALLTHROUGH(op) case AsmJSSimdOperation_##op:
+      case AsmJSSimdType_int32x4:
+        switch (global.simdOperation()) {
+          FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4)
+          default:
+            MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                    "place");
+        }
+        break;
+      case AsmJSSimdType_float32x4:
+        switch (global.simdOperation()) {
+          FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4)
+          default:
+             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                     "place");
+        }
+        break;
+      case AsmJSSimdType_bool32x4:
+        switch (global.simdOperation()) {
+          FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL32X4)