Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Thu, 12 Jul 2018 12:28:13 +0300
changeset 817312 fe17acc6e291e54463db3ea82697c714ae5a4b27
parent 817311 46563760cbb9214f18fcb177d12d09ed94e43f4d (current diff)
parent 817107 1de68d7de52a61407c9155e9bca0def7e2f55c4a (diff)
child 817313 3557e3f6b0afa43a2b1cd1c23bb8b94843aaa7b9
child 817314 431473fb636a36bd63f0952af44b7e4770fc2fd3
child 817315 351fae1c2170646e98aa6fa9829ff07b6340c084
child 817324 013de669fe7c1f3eccee5eb852fdc8012b0ff277
child 817325 fa8fcc0f7dabf662dd65697c829b4ac540552848
child 817326 c1a9891d2d181803dd8885ebcad1b6360f54f77d
child 817362 97b2567d1e3610dabd1ac2e9efe62dfb8a8d28a6
child 817363 73ded8b4bfef814aca91fdc10ee394da009d8b0b
child 817366 57aacc2d1eee39fdbf927602da67fbd0baa62e87
child 817370 7d8db7be8419c2a4a2cb84e4e785e4301efba12b
child 817379 fb56c5cf5e07b11be87511518259b44c5b462111
child 817384 9c3ca2fa2dd2152db85d91aee56eba0e54191bb3
child 817386 d0a25eb8031ec334947f258373df4c781672a3ee
child 817388 381ea5e0115a0eb51bd302327650b14d4730a2cb
child 817389 c40ce75c964c30a7c23eaabbb8d9daba6b453beb
child 817392 bbf69a8b3724e800b6a41542c43e87135559a46e
child 817398 736469ed0fdd60b5d4448a968397be9e34498313
child 817457 3e46f1561ee1990f810e881c7904621129b97592
child 817489 abfa3c60c781bae0ca05ef385f2d19e00979288d
child 817494 c525e29e9adb6b4e92ddde35b9dc4696c571fbd6
child 817501 770fb5cff3e8be1e2191657cc696854e443fdd50
child 817502 4078e8809b6897d76616f4786d4814724c3071ab
child 817504 b0778efb8b128a4c19e7c90ab256a9a75db219c4
child 817512 5458c21802dadad6d198369b5a674ef033908b43
child 817513 5e9618350eb750b773cee8753791182cc90af155
child 817520 4825f60a8632c4dbdaef7390a22158f35a66a0cf
child 817524 be0f6b94f42e81438d038c04361cce0a53179885
child 817527 ff2589dc9933749f061ba9e288485e0e392ba2f7
child 817530 b50adbfdd530d287cdd2c5dc74250108b4a6b68e
child 817531 bd106f26b56565c21bfae8d9984bae5216fd8cb9
child 817552 08943efda0ea3b7fbf923e7afdaee9bb62c8faa1
child 817554 00bf9558a462fe055d63b32ddb70c4f3936785b9
child 817557 745fee354c1b3c12e131bd8e1284b1b3b9551987
child 817602 5db2dfb32d52d1aea702bd8bddce872d48901bde
child 817606 3cca23612efaf523027e0bb8fe8c68149da6575b
child 817779 aa57522a64410243fa5b21679e866c4ed78f3866
child 817781 4697a44f5369279a060d63ac495fdb034e35202b
child 817786 97dde6552592e57c0c46de91e0f5f68d7710989c
child 817826 95ed08f834b924937376808b64949d2717059968
child 817902 c99f0b24a752554069c9a29adb145aae6b41b4d6
child 818013 b7fef8102a5ff13740c4a437ee6f8fd53ef213ab
child 818014 4a0ce6e6e99f65c95abe68969938402b21dbe068
child 818504 a099cd3eedf6785a7af0b95613c5bd210e791a53
child 818722 3ee5a14e7a50179c6fa7179b58887d9c42528f1e
child 818796 13f386b01e1835388ffe75a587943e409f617224
child 818797 edbefc6e6547f6adba204098dd1324faaac93786
child 818829 9d12e228ea47b333e046fc1b1040daee3c332758
child 818958 81a6e3e1f900abebc76367151c88a0184f9d13f0
child 818961 ab69b044f308713fc320a1ba8aa02944ae84e77b
child 819387 95774a855da6b7cc0be0619f2a40a1c7c214d6ab
child 819491 4bff231c917ff9dafc406929a5db684cca024b10
child 819686 5b8d0fb5f3ecca1cc47f1b177b6311bbcefaf29a
child 819778 1cc90c75564d150277979e15f4f2c0bd43f88844
child 820142 c4ce9377dc520417f6a919b6be9e3c0b04579175
child 820383 dfb458a23240798489c326277d3a9fa9994e84fb
child 820419 0fa0e737378e41b8659b2def08ac56be5fd6f054
child 820567 a8865363eacb9f9d963f93ee4aa2595f9330ca60
child 820866 1e54b64aba10250e10656e829d1cbf708a706f16
child 821544 91e5ef72652566a9c79f5ba643687164337bcd8f
child 821554 fe901f5bb95fa5949c917285dae392e31dada2ae
child 822274 da3eb5e151df7ea1edbdf31cf7858caf1672d9c0
child 822275 b6a58b9a15bbce0d8a425d3839d3e8664f4a7100
child 822680 31c0859c5c192ac0ea52212f418805edf0e6de1b
child 823736 4c8496ef42fee75992a449c257d7861ed1a23fec
push id116008
push userbmo:nchevobbe@mozilla.com
push dateThu, 12 Jul 2018 10:22:17 +0000
reviewersmerge
milestone63.0a1
Merge inbound to mozilla-central. a=merge
--- a/dom/base/nsIScriptGlobalObject.h
+++ b/dom/base/nsIScriptGlobalObject.h
@@ -66,21 +66,19 @@ public:
 
   nsIScriptContext* GetContext() {
     return GetScriptContext();
   }
 
   /**
    * Handle a script error.  Generally called by a script context.
    */
-  virtual nsresult HandleScriptError(
-                     const mozilla::dom::ErrorEventInit &aErrorEventInit,
-                     nsEventStatus *aEventStatus) {
-    NS_ENSURE_STATE(NS_HandleScriptError(this, aErrorEventInit, aEventStatus));
-    return NS_OK;
+  bool HandleScriptError(const mozilla::dom::ErrorEventInit &aErrorEventInit,
+                         nsEventStatus *aEventStatus) {
+    return NS_HandleScriptError(this, aErrorEventInit, aEventStatus);
   }
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true) { return false; }
 
 protected:
   virtual ~nsIScriptGlobalObject() {}
 };
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -77,16 +77,20 @@
 #ifdef MOZ_WIDGET_COCOA
 #include "nsCocoaFeatures.h"
 #endif
 
 #ifdef XP_WIN
 #include "WGLLibrary.h"
 #endif
 
+#if defined(MOZ_WIDGET_ANDROID)
+    #include "../../gfx/vr/gfxVRExternal.h"
+#endif
+
 // Generated
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
@@ -2285,16 +2289,65 @@ WebGLContext::GetUnpackSize(bool isFunc3
 
     CheckedUint32 totalBytes = strideBytesPerImage * (usedImages - 1);
     totalBytes += strideBytesPerRow * (usedRowsPerImage - 1);
     totalBytes += usedBytesPerRow;
 
     return totalBytes;
 }
 
+
+#if defined(MOZ_WIDGET_ANDROID)
+already_AddRefed<layers::SharedSurfaceTextureClient>
+WebGLContext::GetVRFrame()
+{
+  if (IsContextLost()) {
+    ForceRestoreContext();
+  }
+
+  int frameId = gfx::impl::VRDisplayExternal::sPushIndex;
+  static int lastFrameId = -1;
+  /**
+   * Android doesn't like duplicated GetVRFrame within the same gfxVRExternal frame.
+   * Ballout forced composition calls if we are in the same VRExternal push frame index.
+   * Also discard frameId 0 because sometimes compositor is not paused yet due to channel communication delays.
+   */
+  const bool ignoreFrame = lastFrameId == frameId || frameId == 0;
+  lastFrameId = frameId;
+  if (!ignoreFrame) {
+      BeginComposition();
+      EndComposition();
+  }
+
+  if (IsContextLost()) {
+    return nullptr;
+  }
+
+  gl::GLScreenBuffer* screen = gl->Screen();
+  if (!screen) {
+    return nullptr;
+  }
+
+  RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
+  if (!sharedSurface || !sharedSurface->Surf()) {
+    return nullptr;
+  }
+
+  /**
+   * Make sure that the WebGL buffer is committed to the attached SurfaceTexture on Android.
+   */
+  if (!ignoreFrame && !IsContextLost()) {
+    sharedSurface->Surf()->ProducerAcquire();
+    sharedSurface->Surf()->Commit();
+    sharedSurface->Surf()->ProducerRelease();
+  }
+
+  return sharedSurface.forget();
+}
+#else
 already_AddRefed<layers::SharedSurfaceTextureClient>
 WebGLContext::GetVRFrame()
 {
   /**
    * Swap buffers as though composition has occurred.
    * We will then share the resulting front buffer to be submitted to the VR
    * compositor.
    */
@@ -2310,16 +2363,18 @@ WebGLContext::GetVRFrame()
 
   RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
   if (!sharedSurface)
       return nullptr;
 
   return sharedSurface.forget();
 }
 
+#endif  // ifdefined(MOZ_WIDGET_ANDROID)
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static inline size_t
 SizeOfViewElem(const dom::ArrayBufferView& view)
 {
     const auto& elemType = view.Type();
     if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
         return 1;
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -557,17 +557,17 @@ IndexedDatabaseManager::CommonPostHandle
   nsEventStatus status = nsEventStatus_eIgnore;
 
   if (NS_IsMainThread()) {
     nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(eventTarget->GetOwnerGlobal());
     if (window) {
       nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
       MOZ_ASSERT(sgo);
 
-      if (NS_WARN_IF(NS_FAILED(sgo->HandleScriptError(init, &status)))) {
+      if (NS_WARN_IF(!sgo->HandleScriptError(init, &status))) {
         status = nsEventStatus_eIgnore;
       }
     } else {
       // We don't fire error events at any global for non-window JS on the main
       // thread.
     }
   } else {
     // Not on the main thread, must be in a worker.
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -280,68 +280,68 @@ VRPose::~VRPose()
   mozilla::DropJSObjects(this);
 }
 
 void
 VRPose::GetPosition(JSContext* aCx,
                     JS::MutableHandle<JSObject*> aRetval,
                     ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mPosition, mVRState.position, 3,
+  SetFloat32Array(aCx, aRetval, mPosition, mVRState.pose.position, 3,
     !mPosition && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position),
     aRv);
 }
 
 void
 VRPose::GetLinearVelocity(JSContext* aCx,
                           JS::MutableHandle<JSObject*> aRetval,
                           ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mLinearVelocity, mVRState.linearVelocity, 3,
+  SetFloat32Array(aCx, aRetval, mLinearVelocity, mVRState.pose.linearVelocity, 3,
     !mLinearVelocity && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position),
     aRv);
 }
 
 void
 VRPose::GetLinearAcceleration(JSContext* aCx,
                               JS::MutableHandle<JSObject*> aRetval,
                               ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mLinearAcceleration, mVRState.linearAcceleration, 3,
+  SetFloat32Array(aCx, aRetval, mLinearAcceleration, mVRState.pose.linearAcceleration, 3,
     !mLinearAcceleration && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration),
     aRv);
 
 }
 
 void
 VRPose::GetOrientation(JSContext* aCx,
                        JS::MutableHandle<JSObject*> aRetval,
                        ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mOrientation, mVRState.orientation, 4,
+  SetFloat32Array(aCx, aRetval, mOrientation, mVRState.pose.orientation, 4,
     !mOrientation && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation),
     aRv);
 }
 
 void
 VRPose::GetAngularVelocity(JSContext* aCx,
                            JS::MutableHandle<JSObject*> aRetval,
                            ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mAngularVelocity, mVRState.angularVelocity, 3,
+  SetFloat32Array(aCx, aRetval, mAngularVelocity, mVRState.pose.angularVelocity, 3,
     !mAngularVelocity && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation),
     aRv);
 }
 
 void
 VRPose::GetAngularAcceleration(JSContext* aCx,
                                JS::MutableHandle<JSObject*> aRetval,
                                ErrorResult& aRv)
 {
-  SetFloat32Array(aCx, aRetval, mAngularAcceleration, mVRState.angularAcceleration, 3,
+  SetFloat32Array(aCx, aRetval, mAngularAcceleration, mVRState.pose.angularAcceleration, 3,
     !mAngularAcceleration && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration),
     aRv);
 }
 
 JSObject*
 VRPose::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return VRPose_Binding::Wrap(aCx, this, aGivenProto);
--- a/dom/vr/VRServiceTest.cpp
+++ b/dom/vr/VRServiceTest.cpp
@@ -90,60 +90,60 @@ VRMockDisplay::SetPose(const Nullable<Fl
                        VRDisplayCapabilityFlags::Cap_External |
                        VRDisplayCapabilityFlags::Cap_MountDetection |
                        VRDisplayCapabilityFlags::Cap_Present;
 
   if (!aOrientation.IsNull()) {
     const Float32Array& value = aOrientation.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 4);
-    mSensorState.orientation[0] = value.Data()[0];
-    mSensorState.orientation[1] = value.Data()[1];
-    mSensorState.orientation[2] = value.Data()[2];
-    mSensorState.orientation[3] = value.Data()[3];
+    mSensorState.pose.orientation[0] = value.Data()[0];
+    mSensorState.pose.orientation[1] = value.Data()[1];
+    mSensorState.pose.orientation[2] = value.Data()[2];
+    mSensorState.pose.orientation[3] = value.Data()[3];
   }
   if (!aAngularVelocity.IsNull()) {
     const Float32Array& value = aAngularVelocity.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.angularVelocity[0] = value.Data()[0];
-    mSensorState.angularVelocity[1] = value.Data()[1];
-    mSensorState.angularVelocity[2] = value.Data()[2];
+    mSensorState.pose.angularVelocity[0] = value.Data()[0];
+    mSensorState.pose.angularVelocity[1] = value.Data()[1];
+    mSensorState.pose.angularVelocity[2] = value.Data()[2];
   }
   if (!aAngularAcceleration.IsNull()) {
     const Float32Array& value = aAngularAcceleration.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.angularAcceleration[0] = value.Data()[0];
-    mSensorState.angularAcceleration[1] = value.Data()[1];
-    mSensorState.angularAcceleration[2] = value.Data()[2];
+    mSensorState.pose.angularAcceleration[0] = value.Data()[0];
+    mSensorState.pose.angularAcceleration[1] = value.Data()[1];
+    mSensorState.pose.angularAcceleration[2] = value.Data()[2];
   }
   if (!aPosition.IsNull()) {
     const Float32Array& value = aPosition.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.position[0] = value.Data()[0];
-    mSensorState.position[1] = value.Data()[1];
-    mSensorState.position[2] = value.Data()[2];
+    mSensorState.pose.position[0] = value.Data()[0];
+    mSensorState.pose.position[1] = value.Data()[1];
+    mSensorState.pose.position[2] = value.Data()[2];
   }
   if (!aLinearVelocity.IsNull()) {
     const Float32Array& value = aLinearVelocity.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.linearVelocity[0] = value.Data()[0];
-    mSensorState.linearVelocity[1] = value.Data()[1];
-    mSensorState.linearVelocity[2] = value.Data()[2];
+    mSensorState.pose.linearVelocity[0] = value.Data()[0];
+    mSensorState.pose.linearVelocity[1] = value.Data()[1];
+    mSensorState.pose.linearVelocity[2] = value.Data()[2];
   }
   if (!aLinearAcceleration.IsNull()) {
     const Float32Array& value = aLinearAcceleration.Value();
     value.ComputeLengthAndData();
     MOZ_ASSERT(value.Length() == 3);
-    mSensorState.linearAcceleration[0] = value.Data()[0];
-    mSensorState.linearAcceleration[1] = value.Data()[1];
-    mSensorState.linearAcceleration[2] = value.Data()[2];
+    mSensorState.pose.linearAcceleration[0] = value.Data()[0];
+    mSensorState.pose.linearAcceleration[1] = value.Data()[1];
+    mSensorState.pose.linearAcceleration[2] = value.Data()[2];
   }
 }
 
 void
 VRMockDisplay::Update()
 {
   gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
 
--- a/dom/workers/WorkerError.cpp
+++ b/dom/workers/WorkerError.cpp
@@ -127,17 +127,17 @@ public:
                                                           &status))) {
             NS_WARNING("Failed to dispatch worker thread error event!");
             status = nsEventStatus_eIgnore;
           }
         }
         else if (nsGlobalWindowInner* win = xpc::WindowOrNull(global)) {
           MOZ_ASSERT(NS_IsMainThread());
 
-          if (NS_FAILED(win->HandleScriptError(init, &status))) {
+          if (!win->HandleScriptError(init, &status)) {
             NS_WARNING("Failed to dispatch main thread error event!");
             status = nsEventStatus_eIgnore;
           }
         }
 
         // Was preventDefault() called?
         if (status == nsEventStatus_eConsumeNoDefault) {
           return;
@@ -380,17 +380,17 @@ WorkerErrorReport::ReportError(JSContext
                                                         &status))) {
           NS_WARNING("Failed to dispatch worker thread error event!");
           status = nsEventStatus_eIgnore;
         }
       }
       else if (nsGlobalWindowInner* win = xpc::WindowOrNull(global)) {
         MOZ_ASSERT(NS_IsMainThread());
 
-        if (NS_FAILED(win->HandleScriptError(init, &status))) {
+        if (!win->HandleScriptError(init, &status)) {
           NS_WARNING("Failed to dispatch main thread error event!");
           status = nsEventStatus_eIgnore;
         }
       }
 
       // Was preventDefault() called?
       if (status == nsEventStatus_eConsumeNoDefault) {
         return;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2290,17 +2290,16 @@ WorkerPrivate::BroadcastErrorToSharedWor
   AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
   GetAllSharedWorkers(sharedWorkers);
 
   if (sharedWorkers.IsEmpty()) {
     return;
   }
 
   AutoTArray<WindowAction, 10> windowActions;
-  nsresult rv;
 
   // First fire the error event at all SharedWorker objects. This may include
   // multiple objects in a single window as well as objects in different
   // windows.
   for (size_t index = 0; index < sharedWorkers.Length(); index++) {
     RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
 
     // May be null.
@@ -2384,19 +2383,18 @@ WorkerPrivate::BroadcastErrorToSharedWor
     RootedDictionary<ErrorEventInit> init(aCx);
     init.mLineno = aReport->mLineNumber;
     init.mFilename = aReport->mFilename;
     init.mMessage = aReport->mMessage;
     init.mCancelable = true;
     init.mBubbles = true;
 
     nsEventStatus status = nsEventStatus_eIgnore;
-    rv = sgo->HandleScriptError(init, &status);
-    if (NS_FAILED(rv)) {
-      ThrowAndReport(windowAction.mWindow, rv);
+    if (!sgo->HandleScriptError(init, &status)) {
+      ThrowAndReport(windowAction.mWindow, NS_ERROR_UNEXPECTED);
       continue;
     }
 
     if (status == nsEventStatus_eConsumeNoDefault) {
       shouldLogErrorToConsole = false;
     }
   }
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1841,18 +1841,20 @@ CompositorOGL::CopyToTarget(DrawTarget* 
 }
 
 void
 CompositorOGL::Pause()
 {
 #ifdef MOZ_WIDGET_ANDROID
   if (!gl() || gl()->IsDestroyed())
     return;
-
-  // ReleaseSurface internally calls MakeCurrent.
+  gl()->MakeCurrent();
+  java::GeckoSurfaceTexture::DestroyUnused((int64_t)mGLContext.get());
+  java::GeckoSurfaceTexture::DetachAllFromGLContext((int64_t)mGLContext.get());
+  // ReleaseSurface internally calls MakeCurrent
   gl()->ReleaseSurface();
 #endif
 }
 
 bool
 CompositorOGL::Resume()
 {
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
--- a/gfx/vr/VRDisplayClient.cpp
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -4,17 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <math.h>
 
 #include "prlink.h"
 #include "prenv.h"
 #include "gfxPrefs.h"
+#include "nsIGlobalObject.h"
+#include "nsRefPtrHashtable.h"
 #include "nsString.h"
+#include "mozilla/dom/GamepadManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIScreenManager.h"
 
 #ifdef XP_WIN
 #include "../layers/d3d11/CompositorD3D11.h"
 #endif
@@ -29,16 +32,17 @@ using namespace mozilla::gfx;
 
 VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
   : mDisplayInfo(aDisplayInfo)
   , bLastEventWasMounted(false)
   , bLastEventWasPresenting(false)
   , mPresentationCount(0)
   , mLastEventFrameId(0)
   , mLastPresentingGeneration(0)
+  , mLastEventControllerState{}
 {
   MOZ_COUNT_CTOR(VRDisplayClient);
 }
 
 VRDisplayClient::~VRDisplayClient() {
   MOZ_COUNT_DTOR(VRDisplayClient);
 }
 
@@ -123,16 +127,150 @@ VRDisplayClient::FireEvents()
     }
   }
 
   // Check if we need to trigger VRDisplay.requestAnimationFrame
   if (mLastEventFrameId != mDisplayInfo.mFrameId) {
     mLastEventFrameId = mDisplayInfo.mFrameId;
     vm->RunFrameRequestCallbacks();
   }
+
+  FireGamepadEvents();
+}
+
+void
+VRDisplayClient::FireGamepadEvents()
+{
+  RefPtr<dom::GamepadManager> gamepadManager(dom::GamepadManager::GetService());
+  if (!gamepadManager) {
+    return;
+  }
+  for (int stateIndex=0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    const VRControllerState& state = mDisplayInfo.mControllerState[stateIndex];
+    const VRControllerState& lastState = mLastEventControllerState[stateIndex];
+    uint32_t gamepadId = mDisplayInfo.mDisplayID * kVRControllerMaxCount + stateIndex;
+    bool bIsNew = false;
+
+    // Send events to notify that controllers are removed
+    if (state.controllerName[0] == '\0') {
+      // Controller is not present
+      if (lastState.controllerName[0] != '\0') {
+        // Controller has been removed
+        dom::GamepadRemoved info;
+        dom::GamepadChangeEventBody body(info);
+        dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+        gamepadManager->Update(event);
+      }
+      // Do not process any further events for removed controllers
+      continue;
+    }
+
+    // Send events to notify that new controllers are added
+    if (lastState.controllerName[0] == '\0') {
+      dom::GamepadAdded info(NS_ConvertUTF8toUTF16(state.controllerName),
+                             dom::GamepadMappingType::_empty,
+                             state.hand,
+                             mDisplayInfo.mDisplayID,
+                             state.numButtons,
+                             state.numAxes,
+                             state.numHaptics);
+      dom::GamepadChangeEventBody body(info);
+      dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+      gamepadManager->Update(event);
+      bIsNew = true;
+    }
+
+    // Send events for handedness changes
+    if (state.hand != lastState.hand) {
+      dom::GamepadHandInformation info(state.hand);
+      dom::GamepadChangeEventBody body(info);
+      dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+      gamepadManager->Update(event);
+    }
+
+    // Send events for axis value changes
+    for (uint32_t axisIndex = 0; axisIndex < state.numAxes; axisIndex++) {
+      if (state.axisValue[axisIndex] != lastState.axisValue[axisIndex]) {
+        dom::GamepadAxisInformation info(axisIndex, state.axisValue[axisIndex]);
+        dom::GamepadChangeEventBody body(info);
+        dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+        gamepadManager->Update(event);
+      }
+    }
+
+    // Send events for trigger, touch, and button value changes
+    if (!bIsNew) {
+      // When a new controller is added, we do not emit button events for
+      // the initial state of the inputs.
+      for (uint32_t buttonIndex = 0; buttonIndex < state.numButtons; buttonIndex++) {
+        bool bPressed = (state.buttonPressed & (1ULL << buttonIndex)) != 0;
+        bool bTouched = (state.buttonTouched & (1ULL << buttonIndex)) != 0;
+        bool bLastPressed = (lastState.buttonPressed & (1ULL << buttonIndex)) != 0;
+        bool bLastTouched = (lastState.buttonTouched & (1ULL << buttonIndex)) != 0;
+
+        if (state.triggerValue[buttonIndex] != lastState.triggerValue[buttonIndex] ||
+            bPressed != bLastPressed ||
+            bTouched != bLastTouched) {
+          dom::GamepadButtonInformation info(buttonIndex, state.triggerValue[buttonIndex], bPressed, bTouched);
+          dom::GamepadChangeEventBody body(info);
+          dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+          gamepadManager->Update(event);
+        }
+      }
+    }
+
+    // Send events for pose changes
+    // Note that VRPose is asserted to be a POD type so memcmp is safe
+    if (state.flags != lastState.flags ||
+        state.isPositionValid != lastState.isPositionValid ||
+        state.isOrientationValid != lastState.isOrientationValid ||
+        memcmp(&state.pose, &lastState.pose, sizeof(VRPose)) != 0) {
+
+      // Convert pose to GamepadPoseState
+      dom::GamepadPoseState poseState;
+      poseState.Clear();
+      poseState.flags = state.flags;
+
+      // Orientation values
+      poseState.isOrientationValid = state.isOrientationValid;
+      poseState.orientation[0] = state.pose.orientation[0];
+      poseState.orientation[1] = state.pose.orientation[1];
+      poseState.orientation[2] = state.pose.orientation[2];
+      poseState.orientation[3] = state.pose.orientation[3];
+      poseState.angularVelocity[0] = state.pose.angularVelocity[0];
+      poseState.angularVelocity[1] = state.pose.angularVelocity[1];
+      poseState.angularVelocity[2] = state.pose.angularVelocity[2];
+      poseState.angularAcceleration[0] = state.pose.angularAcceleration[0];
+      poseState.angularAcceleration[1] = state.pose.angularAcceleration[1];
+      poseState.angularAcceleration[2] = state.pose.angularAcceleration[2];
+
+      // Position values
+      poseState.isPositionValid = state.isPositionValid;
+      poseState.position[0] = state.pose.position[0];
+      poseState.position[1] = state.pose.position[1];
+      poseState.position[2] = state.pose.position[2];
+      poseState.linearVelocity[0] = state.pose.linearVelocity[0];
+      poseState.linearVelocity[1] = state.pose.linearVelocity[1];
+      poseState.linearVelocity[2] = state.pose.linearVelocity[2];
+      poseState.linearAcceleration[0] = state.pose.linearAcceleration[0];
+      poseState.linearAcceleration[1] = state.pose.linearAcceleration[1];
+      poseState.linearAcceleration[2] = state.pose.linearAcceleration[2];
+
+      // Send the event
+      dom::GamepadPoseInformation info(poseState);
+      dom::GamepadChangeEventBody body(info);
+      dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR, body);
+      gamepadManager->Update(event);
+    }
+  }
+
+  // Note that VRControllerState is asserted to be a POD type and memcpy is safe.
+  memcpy(mLastEventControllerState,
+         mDisplayInfo.mControllerState,
+         sizeof(VRControllerState) * kVRControllerMaxCount);
 }
 
 VRHMDSensorState
 VRDisplayClient::GetSensorState()
 {
   return mDisplayInfo.GetSensorState();
 }
 
--- a/gfx/vr/VRDisplayClient.h
+++ b/gfx/vr/VRDisplayClient.h
@@ -46,25 +46,30 @@ public:
 
   bool IsPresentationGenerationCurrent() const;
   void MakePresentationGenerationCurrent();
 
 protected:
   virtual ~VRDisplayClient();
 
   void FireEvents();
+  void FireGamepadEvents();
 
   VRDisplayInfo mDisplayInfo;
 
   bool bLastEventWasMounted;
   bool bLastEventWasPresenting;
 
   int mPresentationCount;
   uint64_t mLastEventFrameId;
   uint32_t mLastPresentingGeneration;
+
+  // Difference between mDisplayInfo.mControllerState and mLastEventControllerState
+  // determines what gamepad events to fire when updated.
+  VRControllerState mLastEventControllerState[kVRControllerMaxCount];
 private:
   VRSubmitFrameResultInfo mSubmitFrameResult;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif /* GFX_VR_DISPLAY_CLIENT_H */
--- a/gfx/vr/VRDisplayHost.cpp
+++ b/gfx/vr/VRDisplayHost.cpp
@@ -347,17 +347,17 @@ VRDisplayHost::CheckClearDisplayInfoDirt
 
 VRControllerHost::VRControllerHost(VRDeviceType aType, dom::GamepadHand aHand,
                                    uint32_t aDisplayID)
  : mControllerInfo{}
  , mVibrateIndex(0)
 {
   MOZ_COUNT_CTOR(VRControllerHost);
   mControllerInfo.mType = aType;
-  mControllerInfo.mControllerState.mHand = aHand;
+  mControllerInfo.mControllerState.hand = aHand;
   mControllerInfo.mMappingType = dom::GamepadMappingType::_empty;
   mControllerInfo.mDisplayID = aDisplayID;
   mControllerInfo.mControllerID = VRSystemManager::AllocateControllerID();
 }
 
 VRControllerHost::~VRControllerHost()
 {
   MOZ_COUNT_DTOR(VRControllerHost);
@@ -367,35 +367,35 @@ const VRControllerInfo&
 VRControllerHost::GetControllerInfo() const
 {
   return mControllerInfo;
 }
 
 void
 VRControllerHost::SetButtonPressed(uint64_t aBit)
 {
-  mControllerInfo.mControllerState.mButtonPressed = aBit;
+  mControllerInfo.mControllerState.buttonPressed = aBit;
 }
 
 uint64_t
 VRControllerHost::GetButtonPressed()
 {
-  return mControllerInfo.mControllerState.mButtonPressed;
+  return mControllerInfo.mControllerState.buttonPressed;
 }
 
 void
 VRControllerHost::SetButtonTouched(uint64_t aBit)
 {
-  mControllerInfo.mControllerState.mButtonTouched = aBit;
+  mControllerInfo.mControllerState.buttonTouched = aBit;
 }
 
 uint64_t
 VRControllerHost::GetButtonTouched()
 {
-  return mControllerInfo.mControllerState.mButtonTouched;
+  return mControllerInfo.mControllerState.buttonTouched;
 }
 
 void
 VRControllerHost::SetPose(const dom::GamepadPoseState& aPose)
 {
   mPose = aPose;
 }
 
@@ -403,17 +403,17 @@ const dom::GamepadPoseState&
 VRControllerHost::GetPose()
 {
   return mPose;
 }
 
 dom::GamepadHand
 VRControllerHost::GetHand()
 {
-  return mControllerInfo.mControllerState.mHand;
+  return mControllerInfo.mControllerState.hand;
 }
 
 void
 VRControllerHost::SetVibrateIndex(uint64_t aIndex)
 {
   mVibrateIndex = aIndex;
 }
 
--- a/gfx/vr/external_api/moz_external_vr.h
+++ b/gfx/vr/external_api/moz_external_vr.h
@@ -19,40 +19,47 @@
 #if defined(__ANDROID__)
 #include <pthread.h>
 #endif // defined(__ANDROID__)
 
 namespace mozilla {
 #ifdef MOZILLA_INTERNAL_API
 namespace dom {
   enum class GamepadHand : uint8_t;
+  enum class GamepadCapabilityFlags : uint16_t;
 }
 #endif //  MOZILLA_INTERNAL_API
 namespace gfx {
 
-static const int32_t kVRExternalVersion = 0;
+static const int32_t kVRExternalVersion = 1;
 
 // We assign VR presentations to groups with a bitmask.
 // Currently, we will only display either content or chrome.
 // Later, we will have more groups to support VR home spaces and
 // multitasking environments.
 // These values are not exposed to regular content and only affect
 // chrome-only API's.  They may be changed at any time.
 static const uint32_t kVRGroupNone = 0;
 static const uint32_t kVRGroupContent = 1 << 0;
 static const uint32_t kVRGroupChrome = 1 << 1;
 static const uint32_t kVRGroupAll = 0xffffffff;
 
 static const int kVRDisplayNameMaxLen = 256;
 static const int kVRControllerNameMaxLen = 256;
 static const int kVRControllerMaxCount = 16;
-static const int kVRControllerMaxTriggers = 16;
+static const int kVRControllerMaxButtons = 64;
 static const int kVRControllerMaxAxis = 16;
 static const int kVRLayerMaxCount = 8;
 
+#if defined(__ANDROID__)
+typedef uint64_t VRLayerTextureHandle;
+#else
+typedef void* VRLayerTextureHandle;
+#endif
+
 struct Point3D_POD
 {
   float x;
   float y;
   float z;
 };
 
 struct IntSize_POD
@@ -71,16 +78,42 @@ struct FloatSize_POD
 
 enum class ControllerHand : uint8_t {
   _empty,
   Left,
   Right,
   EndGuard_
 };
 
+enum class ControllerCapabilityFlags : uint16_t {
+  Cap_None = 0,
+  /**
+   * Cap_Position is set if the Gamepad is capable of tracking its position.
+   */
+  Cap_Position = 1 << 1,
+  /**
+    * Cap_Orientation is set if the Gamepad is capable of tracking its orientation.
+    */
+  Cap_Orientation = 1 << 2,
+  /**
+   * Cap_AngularAcceleration is set if the Gamepad is capable of tracking its
+   * angular acceleration.
+   */
+  Cap_AngularAcceleration = 1 << 3,
+  /**
+   * Cap_LinearAcceleration is set if the Gamepad is capable of tracking its
+   * linear acceleration.
+   */
+  Cap_LinearAcceleration = 1 << 4,
+  /**
+   * Cap_All used for validity checking during IPC serialization
+   */
+  Cap_All = (1 << 5) - 1
+};
+
 #endif // ifndef MOZILLA_INTERNAL_API
 
 enum class VRDisplayCapabilityFlags : uint16_t {
   Cap_None = 0,
   /**
    * Cap_Position is set if the VRDisplay is capable of tracking its position.
    */
   Cap_Position = 1 << 1,
@@ -129,30 +162,35 @@ enum class VRDisplayCapabilityFlags : ui
    */
   Cap_All = (1 << 9) - 1
 };
 
 #ifdef MOZILLA_INTERNAL_API
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
 #endif // MOZILLA_INTERNAL_API
 
+struct VRPose
+{
+  float orientation[4];
+  float position[3];
+  float angularVelocity[3];
+  float angularAcceleration[3];
+  float linearVelocity[3];
+  float linearAcceleration[3];
+};
+
 struct VRHMDSensorState {
-  int64_t inputFrameID;
+  uint64_t inputFrameID;
   double timestamp;
   VRDisplayCapabilityFlags flags;
 
   // These members will only change with inputFrameID:
-  float orientation[4];
-  float position[3];
+  VRPose pose;
   float leftViewMatrix[16];
   float rightViewMatrix[16];
-  float angularVelocity[3];
-  float angularAcceleration[3];
-  float linearVelocity[3];
-  float linearAcceleration[3];
 
 #ifdef MOZILLA_INTERNAL_API
 
   void Clear() {
     memset(this, 0, sizeof(VRHMDSensorState));
   }
 
   bool operator==(const VRHMDSensorState& other) const {
@@ -237,32 +275,40 @@ struct VRDisplayState
   float mSittingToStandingTransform[16];
   uint64_t mLastSubmittedFrameId;
   bool mLastSubmittedFrameSuccessful;
   uint32_t mPresentingGeneration;
 };
 
 struct VRControllerState
 {
-  char mControllerName[kVRControllerNameMaxLen];
+  char controllerName[kVRControllerNameMaxLen];
 #ifdef MOZILLA_INTERNAL_API
-  dom::GamepadHand mHand;
+  dom::GamepadHand hand;
 #else
-  ControllerHand mHand;
+  ControllerHand hand;
 #endif
-  uint32_t mNumButtons;
-  uint32_t mNumAxes;
-  uint32_t mNumTriggers;
-  uint32_t mNumHaptics;
+  uint32_t numButtons;
+  uint32_t numAxes;
+  uint32_t numHaptics;
   // The current button pressed bit of button mask.
-  uint64_t mButtonPressed;
+  uint64_t buttonPressed;
   // The current button touched bit of button mask.
-  uint64_t mButtonTouched;
-  float mTriggerValue[kVRControllerMaxTriggers];
-  float mAxisValue[kVRControllerMaxAxis];
+  uint64_t buttonTouched;
+  float triggerValue[kVRControllerMaxButtons];
+  float axisValue[kVRControllerMaxAxis];
+
+#ifdef MOZILLA_INTERNAL_API
+  dom::GamepadCapabilityFlags flags;
+#else
+  ControllerCapabilityFlags flags;
+#endif
+  VRPose pose;
+  bool isPositionValid;
+  bool isOrientationValid;
 };
 
 struct VRLayerEyeRect
 {
   float x;
   float y;
   float width;
   float height;
@@ -272,29 +318,30 @@ enum class VRLayerType : uint16_t {
   LayerType_None = 0,
   LayerType_2D_Content = 1,
   LayerType_Stereo_Immersive = 2
 };
 
 enum class VRLayerTextureType : uint16_t {
   LayerTextureType_None = 0,
   LayerTextureType_D3D10SurfaceDescriptor = 1,
-  LayerTextureType_MacIOSurface = 2
+  LayerTextureType_MacIOSurface = 2,
+  LayerTextureType_GeckoSurfaceTexture = 3
 };
 
 struct VRLayer_2D_Content
 {
-  void* mTextureHandle;
+  VRLayerTextureHandle mTextureHandle;
   VRLayerTextureType mTextureType;
   uint64_t mFrameId;
 };
 
 struct VRLayer_Stereo_Immersive
 {
-  void* mTextureHandle;
+  VRLayerTextureHandle mTextureHandle;
   VRLayerTextureType mTextureType;
   uint64_t mFrameId;
   uint64_t mInputFrameId;
   VRLayerEyeRect mLeftEyeRect;
   VRLayerEyeRect mRightEyeRect;
 };
 
 struct VRLayerState
@@ -311,30 +358,31 @@ struct VRBrowserState
 #if defined(__ANDROID__)
   bool shutdown;
 #endif // defined(__ANDROID__)
   VRLayerState layerState[kVRLayerMaxCount];
 };
 
 struct VRSystemState
 {
-  uint32_t presentingGeneration;
   bool enumerationCompleted;
   VRDisplayState displayState;
   VRHMDSensorState sensorState;
   VRControllerState controllerState[kVRControllerMaxCount];
 };
 
 struct VRExternalShmem
 {
   int32_t version;
   int32_t size;
 #if defined(__ANDROID__)
   pthread_mutex_t systemMutex;
   pthread_mutex_t browserMutex;
+  pthread_cond_t systemCond;
+  pthread_cond_t browserCond;
 #else
   int64_t generationA;
 #endif // defined(__ANDROID__)
   VRSystemState state;
 #if !defined(__ANDROID__)
   int64_t generationB;
   int64_t browserGenerationA;
 #endif // !defined(__ANDROID__)
--- a/gfx/vr/gfxVR.cpp
+++ b/gfx/vr/gfxVR.cpp
@@ -184,20 +184,20 @@ VRSystemManager::NewHandChangeEvent(uint
 }
 
 void
 VRHMDSensorState::CalcViewMatrices(const gfx::Matrix4x4* aHeadToEyeTransforms)
 {
 
   gfx::Matrix4x4 matHead;
   if (flags & VRDisplayCapabilityFlags::Cap_Orientation) {
-    matHead.SetRotationFromQuaternion(gfx::Quaternion(orientation[0], orientation[1],
-                                                      orientation[2], orientation[3]));
+    matHead.SetRotationFromQuaternion(gfx::Quaternion(pose.orientation[0], pose.orientation[1],
+                                                      pose.orientation[2], pose.orientation[3]));
   }
-  matHead.PreTranslate(-position[0], -position[1], -position[2]);
+  matHead.PreTranslate(-pose.position[0], -pose.position[1], -pose.position[2]);
 
   gfx::Matrix4x4 matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Left];
   matView.Normalize();
   memcpy(leftViewMatrix, matView.components, sizeof(matView.components));
   matView = matHead * aHeadToEyeTransforms[VRDisplayState::Eye_Right];
   matView.Normalize();
   memcpy(rightViewMatrix, matView.components, sizeof(matView.components));
 }
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -55,16 +55,17 @@ enum class VRDeviceType : uint16_t {
 struct VRDisplayInfo
 {
   uint32_t mDisplayID;
   VRDeviceType mType;
   uint32_t mPresentingGroups;
   uint32_t mGroupMask;
   uint64_t mFrameId;
   VRDisplayState mDisplayState;
+  VRControllerState mControllerState[kVRControllerMaxCount];
 
   VRHMDSensorState mLastSensorState[kVRMaxLatencyFrames];
   const VRHMDSensorState& GetSensorState() const
   {
     return mLastSensorState[mFrameId % kVRMaxLatencyFrames];
   }
 
   VRDeviceType GetType() const { return mType; }
@@ -84,20 +85,21 @@ struct VRDisplayInfo
   uint64_t GetFrameId() const { return mFrameId; }
 
   bool operator==(const VRDisplayInfo& other) const {
     for (size_t i = 0; i < kVRMaxLatencyFrames; i++) {
       if (mLastSensorState[i] != other.mLastSensorState[i]) {
         return false;
       }
     }
-    // Note that mDisplayState is asserted to be a POD type, so memcmp is safe
+    // Note that mDisplayState and mControllerState are asserted to be POD types, so memcmp is safe
     return mType == other.mType &&
            mDisplayID == other.mDisplayID &&
            memcmp(&mDisplayState, &other.mDisplayState, sizeof(VRDisplayState)) == 0 &&
+           memcmp(mControllerState, other.mControllerState, sizeof(VRControllerState) * kVRControllerMaxCount) == 0 &&
            mPresentingGroups == other.mPresentingGroups &&
            mGroupMask == other.mGroupMask &&
            mFrameId == other.mFrameId;
   }
 
   bool operator!=(const VRDisplayInfo& other) const {
     return !(*this == other);
   }
@@ -118,39 +120,36 @@ struct VRSubmitFrameResultInfo
   uint32_t mWidth;
   uint32_t mHeight;
 };
 
 struct VRControllerInfo
 {
   VRDeviceType GetType() const { return mType; }
   uint32_t GetControllerID() const { return mControllerID; }
-  const char* GetControllerName() const { return mControllerState.mControllerName; }
+  const char* GetControllerName() const { return mControllerState.controllerName; }
   dom::GamepadMappingType GetMappingType() const { return mMappingType; }
   uint32_t GetDisplayID() const { return mDisplayID; }
-  dom::GamepadHand GetHand() const { return mControllerState.mHand; }
-  uint32_t GetNumButtons() const { return mControllerState.mNumButtons; }
-  uint32_t GetNumAxes() const { return mControllerState.mNumAxes; }
-  uint32_t GetNumHaptics() const { return mControllerState.mNumHaptics; }
+  dom::GamepadHand GetHand() const { return mControllerState.hand; }
+  uint32_t GetNumButtons() const { return mControllerState.numButtons; }
+  uint32_t GetNumAxes() const { return mControllerState.numAxes; }
+  uint32_t GetNumHaptics() const { return mControllerState.numHaptics; }
 
   uint32_t mControllerID;
   VRDeviceType mType;
   dom::GamepadMappingType mMappingType;
   uint32_t mDisplayID;
   VRControllerState mControllerState;
   bool operator==(const VRControllerInfo& other) const {
+    // Note that mControllerState is asserted to be a POD type, so memcmp is safe
     return mType == other.mType &&
            mControllerID == other.mControllerID &&
-           strncmp(mControllerState.mControllerName, other.mControllerState.mControllerName, kVRControllerNameMaxLen) == 0 &&
+           memcmp(&mControllerState, &other.mControllerState, sizeof(VRControllerState)) == 0 &&
            mMappingType == other.mMappingType &&
-           mDisplayID == other.mDisplayID &&
-           mControllerState.mHand == other.mControllerState.mHand &&
-           mControllerState.mNumButtons == other.mControllerState.mNumButtons &&
-           mControllerState.mNumAxes == other.mControllerState.mNumAxes &&
-           mControllerState.mNumHaptics == other.mControllerState.mNumHaptics;
+           mDisplayID == other.mDisplayID;
   }
 
   bool operator!=(const VRControllerInfo& other) const {
     return !(*this == other);
   }
 };
 
 struct VRTelemetry
--- a/gfx/vr/gfxVRExternal.cpp
+++ b/gfx/vr/gfxVRExternal.cpp
@@ -47,70 +47,69 @@ static const char* kShmemName = "/moz.ge
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
-static const uint32_t kNumExternalHaptcs = 1;
+int VRDisplayExternal::sPushIndex = 0;
 
 VRDisplayExternal::VRDisplayExternal(const VRDisplayState& aDisplayState)
   : VRDisplayHost(VRDeviceType::External)
   , mIsPresenting(false)
   , mLastSensorState{}
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
   mDisplayInfo.mDisplayState = aDisplayState;
 
   // default to an identity quaternion
-  mLastSensorState.orientation[3] = 1.0f;
+  mLastSensorState.pose.orientation[3] = 1.0f;
 }
 
 VRDisplayExternal::~VRDisplayExternal()
 {
   Destroy();
   MOZ_COUNT_DTOR_INHERITED(VRDisplayExternal, VRDisplayHost);
 }
 
 void
 VRDisplayExternal::Destroy()
 {
   StopPresentation();
-
-  // TODO - Implement
 }
 
 void
 VRDisplayExternal::ZeroSensor()
 {
 }
 
 void
 VRDisplayExternal::Refresh()
 {
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
 
-  manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState);
+  manager->PullState(&mDisplayInfo.mDisplayState, &mLastSensorState, mDisplayInfo.mControllerState);
 }
 
 VRHMDSensorState
 VRDisplayExternal::GetSensorState()
 {
   return mLastSensorState;
 }
 
 void
 VRDisplayExternal::StartPresentation()
 {
   if (mIsPresenting) {
     return;
   }
+  sPushIndex = 0;
   mIsPresenting = true;
   mTelemetry.Clear();
   mTelemetry.mPresentationStart = TimeStamp::Now();
 
   // Indicate that we are ready to start immersive mode
   VRBrowserState state;
   memset(&state, 0, sizeof(VRBrowserState));
   state.layerState[0].type = VRLayerType::LayerType_Stereo_Immersive;
@@ -125,23 +124,24 @@ VRDisplayExternal::StartPresentation()
 
 void
 VRDisplayExternal::StopPresentation()
 {
   if (!mIsPresenting) {
     return;
   }
   mIsPresenting = false;
+  sPushIndex = 0;
 
   // Indicate that we have stopped immersive mode
   VRBrowserState state;
   memset(&state, 0, sizeof(VRBrowserState));
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
-  manager->PushState(&state);
+  manager->PushState(&state, true);
 
   // TODO - Implement telemetry:
 
 /*
   const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
   Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
   Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
                         duration.ToMilliseconds());
@@ -152,17 +152,17 @@ VRDisplayExternal::StopPresentation()
                                         mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
   Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
 */
 }
 
 bool
 VRDisplayExternal::PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
                                         VRLayerTextureType* aTextureType,
-                                        void** aTextureHandle)
+                                        VRLayerTextureHandle* aTextureHandle)
 {
   switch (aTexture.type()) {
 #if defined(XP_WIN)
     case SurfaceDescriptor::TSurfaceDescriptorD3D10: {
       const SurfaceDescriptorD3D10& surf = aTexture.get_SurfaceDescriptorD3D10();
       *aTextureType = VRLayerTextureType::LayerTextureType_D3D10SurfaceDescriptor;
       *aTextureHandle = (void *)surf.handle();
       return true;
@@ -176,16 +176,28 @@ VRDisplayExternal::PopulateLayerTexture(
       if (!surf) {
         NS_WARNING("VRDisplayHost::SubmitFrame failed to get a MacIOSurface");
         return false;
       }
       *aTextureType = VRLayerTextureType::LayerTextureType_MacIOSurface;
       *aTextureHandle = (void *)surf->GetIOSurfacePtr();
       return true;
     }
+#elif defined(MOZ_WIDGET_ANDROID)
+    case SurfaceDescriptor::TSurfaceTextureDescriptor: {
+      const SurfaceTextureDescriptor& desc = aTexture.get_SurfaceTextureDescriptor();
+      java::GeckoSurfaceTexture::LocalRef surfaceTexture = java::GeckoSurfaceTexture::Lookup(desc.handle());
+      if (!surfaceTexture) {
+        NS_WARNING("VRDisplayHost::SubmitFrame failed to get a SurfaceTexture");
+        return false;
+      }
+      *aTextureType = VRLayerTextureType::LayerTextureType_GeckoSurfaceTexture;
+      *aTextureHandle = desc.handle();
+      return true;
+    }
 #endif
     default: {
       MOZ_ASSERT(false);
       return false;
     }
   }
 }
 
@@ -211,60 +223,43 @@ VRDisplayExternal::SubmitFrame(const lay
   layer.mLeftEyeRect.height = aLeftEyeRect.height;
   layer.mRightEyeRect.x = aRightEyeRect.x;
   layer.mRightEyeRect.y = aRightEyeRect.y;
   layer.mRightEyeRect.width = aRightEyeRect.width;
   layer.mRightEyeRect.height = aRightEyeRect.height;
 
   VRManager *vm = VRManager::Get();
   VRSystemManagerExternal* manager = vm->GetExternalManager();
-  manager->PushState(&state);
+  manager->PushState(&state, true);
+  sPushIndex++;
 
   VRDisplayState displayState;
   memset(&displayState, 0, sizeof(VRDisplayState));
   while (displayState.mLastSubmittedFrameId < aFrameId) {
-    if (manager->PullState(&displayState, &mLastSensorState)) {
+    if (manager->PullState(&displayState, &mLastSensorState, mDisplayInfo.mControllerState)) {
       if (!displayState.mIsConnected) {
         // Service has shut down or hardware has been disconnected
         return false;
       }
     }
 #ifdef XP_WIN
     Sleep(0);
 #else
     sleep(0);
 #endif
   }
 
   return displayState.mLastSubmittedFrameSuccessful;
 }
 
-VRControllerExternal::VRControllerExternal(dom::GamepadHand aHand, uint32_t aDisplayID,
-                                       uint32_t aNumButtons, uint32_t aNumTriggers,
-                                       uint32_t aNumAxes, const nsCString& aId)
-  : VRControllerHost(VRDeviceType::External, aHand, aDisplayID)
-{
-  MOZ_COUNT_CTOR_INHERITED(VRControllerExternal, VRControllerHost);
-
-  VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.mControllerName, aId.BeginReading(), kVRControllerNameMaxLen);
-  state.mNumButtons = aNumButtons;
-  state.mNumAxes = aNumAxes;
-  state.mNumTriggers = aNumTriggers;
-  state.mNumHaptics = kNumExternalHaptcs;
-}
-
-VRControllerExternal::~VRControllerExternal()
-{
-  MOZ_COUNT_DTOR_INHERITED(VRControllerExternal, VRControllerHost);
-}
-
 VRSystemManagerExternal::VRSystemManagerExternal(VRExternalShmem* aAPIShmem /* = nullptr*/)
  : mExternalShmem(aAPIShmem)
+#if !defined(MOZ_WIDGET_ANDROID)
  , mSameProcess(aAPIShmem != nullptr)
+#endif
 {
 #if defined(XP_MACOSX)
   mShmemFD = 0;
 #elif defined(XP_WIN)
   mShmemFile = NULL;
 #elif defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
   mExternalStructFailed = false;
@@ -335,22 +330,31 @@ VRSystemManagerExternal::OpenShmem()
     CloseShmem();
     return;
   }
 #elif defined(MOZ_WIDGET_ANDROID)
   mExternalShmem = (VRExternalShmem*)mozilla::GeckoVRManager::GetExternalContext();
   if (!mExternalShmem) {
     return;
   }
-  if (mExternalShmem->version != kVRExternalVersion) {
+  int32_t version = -1;
+  int32_t size = 0;
+  if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
+    version = mExternalShmem->version;
+    size = mExternalShmem->size;
+    pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+  } else {
+    return;
+  }
+  if (version != kVRExternalVersion) {
     mExternalShmem = nullptr;
     mExternalStructFailed = true;
     return;
   }
-  if (mExternalShmem->size != sizeof(VRExternalShmem)) {
+  if (size != sizeof(VRExternalShmem)) {
     mExternalShmem = nullptr;
     mExternalStructFailed = true;
     return;
   }
 #endif
   CheckForShutdown();
 }
 
@@ -368,19 +372,21 @@ VRSystemManagerExternal::CheckForShutdow
     }
   }
 #endif // defined(MOZ_WIDGET_ANDROID)
 }
 
 void
 VRSystemManagerExternal::CloseShmem()
 {
+#if !defined(MOZ_WIDGET_ANDROID)
   if (mSameProcess) {
     return;
   }
+#endif
 #if defined(XP_MACOSX)
   if (mExternalShmem) {
     munmap((void *)mExternalShmem, sizeof(VRExternalShmem));
     mExternalShmem = NULL;
   }
   if (mShmemFD) {
     close(mShmemFD);
   }
@@ -423,17 +429,16 @@ VRSystemManagerExternal::Destroy()
 }
 
 void
 VRSystemManagerExternal::Shutdown()
 {
   if (mDisplay) {
     mDisplay = nullptr;
   }
-  RemoveControllers();
   CloseShmem();
 #if defined(MOZ_WIDGET_ANDROID)
   mDoShutdown = false;
 #endif
 }
 
 void
 VRSystemManagerExternal::NotifyVSync()
@@ -503,22 +508,16 @@ VRSystemManagerExternal::GetIsPresenting
     VRDisplayInfo displayInfo(mDisplay->GetDisplayInfo());
     return displayInfo.GetPresentingGroups() != 0;
   }
 
   return false;
 }
 
 void
-VRSystemManagerExternal::HandleInput()
-{
-  // TODO - Implement This!
-}
-
-void
 VRSystemManagerExternal::VibrateHaptic(uint32_t aControllerIdx,
                                       uint32_t aHapticIndex,
                                       double aIntensity,
                                       double aDuration,
                                       const VRManagerPromise& aPromise)
 {
   // TODO - Implement this
 }
@@ -527,80 +526,98 @@ void
 VRSystemManagerExternal::StopVibrateHaptic(uint32_t aControllerIdx)
 {
   // TODO - Implement this
 }
 
 void
 VRSystemManagerExternal::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
 {
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
   aControllerResult.Clear();
-  for (uint32_t i = 0; i < mExternalController.Length(); ++i) {
-    aControllerResult.AppendElement(mExternalController[i]);
-  }
 }
 
 void
 VRSystemManagerExternal::ScanForControllers()
 {
-  // TODO - Implement this
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
+  if (mDisplay) {
+    mDisplay->Refresh();
+  }
+  return;
+}
+
+void
+VRSystemManagerExternal::HandleInput()
+{
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
+  if (mDisplay) {
+    mDisplay->Refresh();
+  }
+  return;
 }
 
 void
 VRSystemManagerExternal::RemoveControllers()
 {
-  // The controller count is changed, removing the existing gamepads first.
-  for (uint32_t i = 0; i < mExternalController.Length(); ++i) {
-    RemoveGamepad(i);
-  }
-  mExternalController.Clear();
-  mControllerCount = 0;
+  // Controller updates are handled in VRDisplayClient for VRSystemManagerExternal
 }
 
 bool
-VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState /* = nullptr */)
+VRSystemManagerExternal::PullState(VRDisplayState* aDisplayState,
+                                   VRHMDSensorState* aSensorState /* = nullptr */,
+                                   VRControllerState* aControllerState /* = nullptr */)
 {
   bool success = false;
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == 0) {
-        memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
-        if (aSensorState) {
-          memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
-        }
-        pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
-        mDoShutdown = aDisplayState->shutdown;
-        success = mExternalShmem->state.enumerationCompleted;
+      memcpy(aDisplayState, (void*)&(mExternalShmem->state.displayState), sizeof(VRDisplayState));
+      if (aSensorState) {
+        memcpy(aSensorState, (void*)&(mExternalShmem->state.sensorState), sizeof(VRHMDSensorState));
+      }
+      if (aControllerState) {
+        memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
+      }
+      success = mExternalShmem->state.enumerationCompleted;
+      pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex));
+      mDoShutdown = aDisplayState->shutdown;
     }
 #else
     VRExternalShmem tmp;
     memcpy(&tmp, (void *)mExternalShmem, sizeof(VRExternalShmem));
     if (tmp.generationA == tmp.generationB && tmp.generationA != 0 && tmp.generationA != -1 && tmp.state.enumerationCompleted) {
       memcpy(aDisplayState, &tmp.state.displayState, sizeof(VRDisplayState));
       if (aSensorState) {
         memcpy(aSensorState, &tmp.state.sensorState, sizeof(VRHMDSensorState));
       }
+      if (aControllerState) {
+        memcpy(aControllerState, (void*)&(mExternalShmem->state.controllerState), sizeof(VRControllerState) * kVRControllerMaxCount);
+      }
       success = true;
     }
 #endif // defined(MOZ_WIDGET_ANDROID)
   }
 
   return success;
 }
 
 void
-VRSystemManagerExternal::PushState(VRBrowserState* aBrowserState)
+VRSystemManagerExternal::PushState(VRBrowserState* aBrowserState, bool aNotifyCond)
 {
   MOZ_ASSERT(aBrowserState);
   MOZ_ASSERT(mExternalShmem);
   if (mExternalShmem) {
 #if defined(MOZ_WIDGET_ANDROID)
     if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->browserMutex)) == 0) {
       memcpy((void *)&(mExternalShmem->browserState), aBrowserState, sizeof(VRBrowserState));
+      if (aNotifyCond) {
+        pthread_cond_signal((pthread_cond_t*)&(mExternalShmem->browserCond));
+      }
       pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->browserMutex));
     }
 #else
     mExternalShmem->browserGenerationA++;
     memcpy((void *)&(mExternalShmem->browserState), (void *)aBrowserState, sizeof(VRBrowserState));
     mExternalShmem->browserGenerationB++;
 #endif // defined(MOZ_WIDGET_ANDROID)
   }
--- a/gfx/vr/gfxVRExternal.h
+++ b/gfx/vr/gfxVRExternal.h
@@ -26,55 +26,46 @@ namespace gfx {
 class VRThread;
 
 namespace impl {
 
 class VRDisplayExternal : public VRDisplayHost
 {
 public:
   void ZeroSensor() override;
+  static int sPushIndex;
 
 protected:
   VRHMDSensorState GetSensorState() override;
   void StartPresentation() override;
   void StopPresentation() override;
 
   bool SubmitFrame(const layers::SurfaceDescriptor& aTexture,
                    uint64_t aFrameId,
                    const gfx::Rect& aLeftEyeRect,
                    const gfx::Rect& aRightEyeRect) override;
 
 public:
   explicit VRDisplayExternal(const VRDisplayState& aDisplayState);
   void Refresh();
+  const VRControllerState& GetLastControllerState(uint32_t aStateIndex) const;
 protected:
   virtual ~VRDisplayExternal();
   void Destroy();
 
 private:
   bool PopulateLayerTexture(const layers::SurfaceDescriptor& aTexture,
                             VRLayerTextureType* aTextureType,
-                            void** aTextureHandle);
+                            VRLayerTextureHandle* aTextureHandle);
 
   VRTelemetry mTelemetry;
   bool mIsPresenting;
   VRHMDSensorState mLastSensorState;
 };
 
-class VRControllerExternal : public VRControllerHost
-{
-public:
-  explicit VRControllerExternal(dom::GamepadHand aHand, uint32_t aDisplayID, uint32_t aNumButtons,
-                              uint32_t aNumTriggers, uint32_t aNumAxes,
-                              const nsCString& aId);
-
-protected:
-  virtual ~VRControllerExternal();
-};
-
 } // namespace impl
 
 class VRSystemManagerExternal : public VRSystemManager
 {
 public:
   static already_AddRefed<VRSystemManagerExternal> Create(VRExternalShmem* aAPIShmem = nullptr);
 
   virtual void Destroy() override;
@@ -90,38 +81,41 @@ public:
   virtual void ScanForControllers() override;
   virtual void RemoveControllers() override;
   virtual void VibrateHaptic(uint32_t aControllerIdx,
                              uint32_t aHapticIndex,
                              double aIntensity,
                              double aDuration,
                              const VRManagerPromise& aPromise) override;
   virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
-  bool PullState(VRDisplayState* aDisplayState, VRHMDSensorState* aSensorState = nullptr);
-  void PushState(VRBrowserState* aBrowserState);
+  bool PullState(VRDisplayState* aDisplayState,
+                 VRHMDSensorState* aSensorState = nullptr,
+                 VRControllerState* aControllerState = nullptr);
+  void PushState(VRBrowserState* aBrowserState, const bool aNotifyCond = false);
 
 protected:
   explicit VRSystemManagerExternal(VRExternalShmem* aAPIShmem = nullptr);
   virtual ~VRSystemManagerExternal();
 
 private:
   // there can only be one
   RefPtr<impl::VRDisplayExternal> mDisplay;
-  nsTArray<RefPtr<impl::VRControllerExternal>> mExternalController;
 #if defined(XP_MACOSX)
   int mShmemFD;
 #elif defined(XP_WIN)
   HANDLE mShmemFile;
 #elif defined(MOZ_WIDGET_ANDROID)
   bool mDoShutdown;
   bool mExternalStructFailed;
 #endif
 
   volatile VRExternalShmem* mExternalShmem;
+#if !defined(MOZ_WIDGET_ANDROID)
   bool mSameProcess;
+#endif
 
   void OpenShmem();
   void CloseShmem();
   void CheckForShutdown();
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/gfxVROSVR.cpp
+++ b/gfx/vr/gfxVROSVR.cpp
@@ -312,32 +312,32 @@ VRDisplayOSVR::GetSensorState()
   OSVR_ReturnCode ret =
     osvr_GetOrientationState(*m_iface, &timestamp, &orientation);
 
   result.timestamp = timestamp.seconds;
   result.inputFrameID = mDisplayInfo.mFrameId;
 
   if (ret == OSVR_RETURN_SUCCESS) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    result.orientation[0] = orientation.data[1];
-    result.orientation[1] = orientation.data[2];
-    result.orientation[2] = orientation.data[3];
-    result.orientation[3] = orientation.data[0];
+    result.pose.orientation[0] = orientation.data[1];
+    result.pose.orientation[1] = orientation.data[2];
+    result.pose.orientation[2] = orientation.data[3];
+    result.pose.orientation[3] = orientation.data[0];
   } else {
     // default to an identity quaternion
-    result.orientation[3] = 1.0f;
+    result.pose.orientation[3] = 1.0f;
   }
 
   OSVR_PositionState position;
   ret = osvr_GetPositionState(*m_iface, &timestamp, &position);
   if (ret == OSVR_RETURN_SUCCESS) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
-    result.position[0] = position.data[0];
-    result.position[1] = position.data[1];
-    result.position[2] = position.data[2];
+    result.pose.position[0] = position.data[0];
+    result.pose.position[1] = position.data[1];
+    result.pose.position[2] = position.data[2];
   }
 
   result.CalcViewMatrices(mHeadToEye);
 
   return result;
 }
 
 #if defined(XP_WIN)
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -947,17 +947,17 @@ VRDisplayOculus::GetSensorState()
     UpdateEyeParameters(headToEyeTransforms);
     double predictedFrameTime = 0.0f;
     if (gfxPrefs::VRPosePredictionEnabled()) {
       // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't use the result.
       // If we don't call it, the Oculus driver will spew out many warnings...
       predictedFrameTime = ovr_GetPredictedDisplayTime(mSession->Get(), 0);
     }
     result = GetSensorState(predictedFrameTime);
-    result.position[1] -= mEyeHeight;
+    result.pose.position[1] -= mEyeHeight;
     result.CalcViewMatrices(headToEyeTransforms);
   }
   result.inputFrameID = mDisplayInfo.mFrameId;
 
   return result;
 }
 
 VRHMDSensorState
@@ -968,51 +968,51 @@ VRDisplayOculus::GetSensorState(double a
   ovrTrackingState state = ovr_GetTrackingState(mSession->Get(), absTime, true);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
 
-    result.orientation[0] = pose.ThePose.Orientation.x;
-    result.orientation[1] = pose.ThePose.Orientation.y;
-    result.orientation[2] = pose.ThePose.Orientation.z;
-    result.orientation[3] = pose.ThePose.Orientation.w;
+    result.pose.orientation[0] = pose.ThePose.Orientation.x;
+    result.pose.orientation[1] = pose.ThePose.Orientation.y;
+    result.pose.orientation[2] = pose.ThePose.Orientation.z;
+    result.pose.orientation[3] = pose.ThePose.Orientation.w;
 
-    result.angularVelocity[0] = pose.AngularVelocity.x;
-    result.angularVelocity[1] = pose.AngularVelocity.y;
-    result.angularVelocity[2] = pose.AngularVelocity.z;
+    result.pose.angularVelocity[0] = pose.AngularVelocity.x;
+    result.pose.angularVelocity[1] = pose.AngularVelocity.y;
+    result.pose.angularVelocity[2] = pose.AngularVelocity.z;
 
     result.flags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
 
-    result.angularAcceleration[0] = pose.AngularAcceleration.x;
-    result.angularAcceleration[1] = pose.AngularAcceleration.y;
-    result.angularAcceleration[2] = pose.AngularAcceleration.z;
+    result.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
+    result.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
+    result.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
   } else {
     // default to an identity quaternion
-    result.orientation[3] = 1.0f;
+    result.pose.orientation[3] = 1.0f;
   }
 
   if (state.StatusFlags & ovrStatus_PositionTracked) {
     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
 
-    result.position[0] = pose.ThePose.Position.x;
-    result.position[1] = pose.ThePose.Position.y;
-    result.position[2] = pose.ThePose.Position.z;
+    result.pose.position[0] = pose.ThePose.Position.x;
+    result.pose.position[1] = pose.ThePose.Position.y;
+    result.pose.position[2] = pose.ThePose.Position.z;
 
-    result.linearVelocity[0] = pose.LinearVelocity.x;
-    result.linearVelocity[1] = pose.LinearVelocity.y;
-    result.linearVelocity[2] = pose.LinearVelocity.z;
+    result.pose.linearVelocity[0] = pose.LinearVelocity.x;
+    result.pose.linearVelocity[1] = pose.LinearVelocity.y;
+    result.pose.linearVelocity[2] = pose.LinearVelocity.z;
 
     result.flags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
 
-    result.linearAcceleration[0] = pose.LinearAcceleration.x;
-    result.linearAcceleration[1] = pose.LinearAcceleration.y;
-    result.linearAcceleration[2] = pose.LinearAcceleration.z;
+    result.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
+    result.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
+    result.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
   }
   result.flags |= VRDisplayCapabilityFlags::Cap_External;
   result.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
   result.flags |= VRDisplayCapabilityFlags::Cap_Present;
 
   return result;
 }
 
@@ -1325,27 +1325,27 @@ VRControllerOculus::VRControllerOculus(d
     case dom::GamepadHand::Right:
       touchID = "Oculus Touch (Right)";
       break;
     default:
       MOZ_ASSERT(false);
       break;
   }
 
-  strncpy(state.mControllerName, touchID, kVRControllerNameMaxLen);
+  strncpy(state.controllerName, touchID, kVRControllerNameMaxLen);
 
   MOZ_ASSERT(kNumOculusButton ==
              static_cast<uint32_t>(OculusLeftControllerButtonType::NumButtonType)
              && kNumOculusButton ==
              static_cast<uint32_t>(OculusRightControllerButtonType::NumButtonType));
 
-  state.mNumButtons = kNumOculusButton;
-  state.mNumAxes = static_cast<uint32_t>(
+  state.numButtons = kNumOculusButton;
+  state.numAxes = static_cast<uint32_t>(
                    OculusControllerAxisType::NumVRControllerAxisType);
-  state.mNumHaptics = kNumOculusHaptcs;
+  state.numHaptics = kNumOculusHaptcs;
 }
 
 float
 VRControllerOculus::GetAxisMove(uint32_t aAxis)
 {
   return mAxisMove[aAxis];
 }
 
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -282,34 +282,34 @@ VRDisplayOpenVR::GetSensorState()
     memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
     m.Transpose();
 
     gfx::Quaternion rot;
     rot.SetFromRotationMatrix(m);
     rot.Invert();
 
     result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    result.orientation[0] = rot.x;
-    result.orientation[1] = rot.y;
-    result.orientation[2] = rot.z;
-    result.orientation[3] = rot.w;
-    result.angularVelocity[0] = pose.vAngularVelocity.v[0];
-    result.angularVelocity[1] = pose.vAngularVelocity.v[1];
-    result.angularVelocity[2] = pose.vAngularVelocity.v[2];
+    result.pose.orientation[0] = rot.x;
+    result.pose.orientation[1] = rot.y;
+    result.pose.orientation[2] = rot.z;
+    result.pose.orientation[3] = rot.w;
+    result.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+    result.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+    result.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
 
     result.flags |= VRDisplayCapabilityFlags::Cap_Position;
-    result.position[0] = m._41;
-    result.position[1] = m._42;
-    result.position[2] = m._43;
-    result.linearVelocity[0] = pose.vVelocity.v[0];
-    result.linearVelocity[1] = pose.vVelocity.v[1];
-    result.linearVelocity[2] = pose.vVelocity.v[2];
+    result.pose.position[0] = m._41;
+    result.pose.position[1] = m._42;
+    result.pose.position[2] = m._43;
+    result.pose.linearVelocity[0] = pose.vVelocity.v[0];
+    result.pose.linearVelocity[1] = pose.vVelocity.v[1];
+    result.pose.linearVelocity[2] = pose.vVelocity.v[2];
   } else {
     // default to an identity quaternion
-    result.orientation[3] = 1.0f;
+    result.pose.orientation[3] = 1.0f;
   }
 
   result.CalcViewMatrices(headToEyeTransforms);
   result.inputFrameID = mDisplayInfo.mFrameId;
   return result;
 }
 
 void
@@ -433,21 +433,20 @@ VRControllerOpenVR::VRControllerOpenVR(d
   : VRControllerHost(VRDeviceType::OpenVR, aHand, aDisplayID)
   , mTrackedIndex(0)
   , mVibrateThread(nullptr)
   , mIsVibrateStopped(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
 
   VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.mControllerName, aId.BeginReading(), kVRControllerNameMaxLen);
-  state.mNumButtons = aNumButtons;
-  state.mNumAxes = aNumAxes;
-  state.mNumTriggers = aNumTriggers;
-  state.mNumHaptics = kNumOpenVRHaptcs;
+  strncpy(state.controllerName, aId.BeginReading(), kVRControllerNameMaxLen);
+  state.numButtons = aNumButtons;
+  state.numAxes = aNumAxes;
+  state.numHaptics = kNumOpenVRHaptcs;
 }
 
 VRControllerOpenVR::~VRControllerOpenVR()
 {
   ShutdownVibrateHapticThread();
   MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
 }
 
@@ -461,41 +460,41 @@ uint32_t
 VRControllerOpenVR::GetTrackedIndex()
 {
   return mTrackedIndex;
 }
 
 float
 VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
 {
-  return mControllerInfo.mControllerState.mAxisValue[aAxis];
+  return mControllerInfo.mControllerState.axisValue[aAxis];
 }
 
 void
 VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
 {
-  mControllerInfo.mControllerState.mAxisValue[aAxis] = aValue;
+  mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
 }
 
 void
 VRControllerOpenVR::SetTrigger(uint32_t aButton, float aValue)
 {
-  mControllerInfo.mControllerState.mTriggerValue[aButton] = aValue;
+  mControllerInfo.mControllerState.triggerValue[aButton] = aValue;
 }
 
 float
 VRControllerOpenVR::GetTrigger(uint32_t aButton)
 {
-  return mControllerInfo.mControllerState.mTriggerValue[aButton];
+  return mControllerInfo.mControllerState.triggerValue[aButton];
 }
 
 void
 VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
 {
-  mControllerInfo.mControllerState.mHand = aHand;
+  mControllerInfo.mControllerState.hand = aHand;
 }
 
 void
 VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
                                         uint32_t aHapticIndex,
                                         double aIntensity,
                                         double aDuration,
                                         uint64_t aVibrateIndex,
--- a/gfx/vr/gfxVRPuppet.cpp
+++ b/gfx/vr/gfxVRPuppet.cpp
@@ -99,31 +99,31 @@ VRDisplayPuppet::VRDisplayPuppet()
   state.mSittingToStandingTransform[12] = 0.0f;
   state.mSittingToStandingTransform[13] = 0.75f;
   state.mSittingToStandingTransform[14] = 0.0f;
   state.mSittingToStandingTransform[15] = 1.0f;
 
   gfx::Quaternion rot;
 
   mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-  mSensorState.orientation[0] = rot.x;
-  mSensorState.orientation[1] = rot.y;
-  mSensorState.orientation[2] = rot.z;
-  mSensorState.orientation[3] = rot.w;
-  mSensorState.angularVelocity[0] = 0.0f;
-  mSensorState.angularVelocity[1] = 0.0f;
-  mSensorState.angularVelocity[2] = 0.0f;
+  mSensorState.pose.orientation[0] = rot.x;
+  mSensorState.pose.orientation[1] = rot.y;
+  mSensorState.pose.orientation[2] = rot.z;
+  mSensorState.pose.orientation[3] = rot.w;
+  mSensorState.pose.angularVelocity[0] = 0.0f;
+  mSensorState.pose.angularVelocity[1] = 0.0f;
+  mSensorState.pose.angularVelocity[2] = 0.0f;
 
   mSensorState.flags |= VRDisplayCapabilityFlags::Cap_Position;
-  mSensorState.position[0] = 0.0f;
-  mSensorState.position[1] = 0.0f;
-  mSensorState.position[2] = 0.0f;
-  mSensorState.linearVelocity[0] = 0.0f;
-  mSensorState.linearVelocity[1] = 0.0f;
-  mSensorState.linearVelocity[2] = 0.0f;
+  mSensorState.pose.position[0] = 0.0f;
+  mSensorState.pose.position[1] = 0.0f;
+  mSensorState.pose.position[2] = 0.0f;
+  mSensorState.pose.linearVelocity[0] = 0.0f;
+  mSensorState.pose.linearVelocity[1] = 0.0f;
+  mSensorState.pose.linearVelocity[2] = 0.0f;
 }
 
 VRDisplayPuppet::~VRDisplayPuppet()
 {
   MOZ_COUNT_DTOR_INHERITED(VRDisplayPuppet, VRDisplayLocal);
 }
 
 void
@@ -580,20 +580,20 @@ VRDisplayPuppet::Refresh()
 
 VRControllerPuppet::VRControllerPuppet(dom::GamepadHand aHand, uint32_t aDisplayID)
   : VRControllerHost(VRDeviceType::Puppet, aHand, aDisplayID)
   , mButtonPressState(0)
   , mButtonTouchState(0)
 {
   MOZ_COUNT_CTOR_INHERITED(VRControllerPuppet, VRControllerHost);
   VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.mControllerName, "Puppet Gamepad", kVRControllerNameMaxLen);
-  state.mNumButtons = kNumPuppetButtonMask;
-  state.mNumAxes = kNumPuppetAxis;
-  state.mNumHaptics = kNumPuppetHaptcs;
+  strncpy(state.controllerName, "Puppet Gamepad", kVRControllerNameMaxLen);
+  state.numButtons = kNumPuppetButtonMask;
+  state.numAxes = kNumPuppetAxis;
+  state.numHaptics = kNumPuppetHaptcs;
 }
 
 VRControllerPuppet::~VRControllerPuppet()
 {
   MOZ_COUNT_DTOR_INHERITED(VRControllerPuppet, VRControllerHost);
 }
 
 void
@@ -667,23 +667,23 @@ const dom::GamepadPoseState&
 VRControllerPuppet::GetPoseMoveState()
 {
   return mPoseState;
 }
 
 float
 VRControllerPuppet::GetAxisMove(uint32_t aAxis)
 {
-  return mControllerInfo.mControllerState.mAxisValue[aAxis];
+  return mControllerInfo.mControllerState.axisValue[aAxis];
 }
 
 void
 VRControllerPuppet::SetAxisMove(uint32_t aAxis, float aValue)
 {
-  mControllerInfo.mControllerState.mAxisValue[aAxis] = aValue;
+  mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
 }
 
 VRSystemManagerPuppet::VRSystemManagerPuppet()
   : mPuppetDisplayCount(0)
   , mPuppetDisplayInfo{}
   , mPuppetDisplaySensorState{}
 {
 }
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -39,16 +39,17 @@ void ReleaseVRManagerParentSingleton() {
 
 VRManagerChild::VRManagerChild()
   : mDisplaysInitialized(false)
   , mMessageLoop(MessageLoop::current())
   , mFrameRequestCallbackCounter(0)
   , mBackend(layers::LayersBackend::LAYERS_NONE)
   , mPromiseID(0)
   , mVRMockDisplay(nullptr)
+  , mLastControllerState{}
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mStartTimeStamp = TimeStamp::Now();
 }
 
 VRManagerChild::~VRManagerChild()
 {
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -134,16 +134,17 @@ private:
   nsTArray<RefPtr<dom::VREventObserver>> mListeners;
 
   layers::LayersBackend mBackend;
   RefPtr<layers::SyncObjectClient> mSyncObject;
   nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mGamepadPromiseList;
   uint32_t mPromiseID;
   nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mPromiseList;
   RefPtr<dom::VRMockDisplay> mVRMockDisplay;
+  VRControllerState mLastControllerState[kVRControllerMaxCount];
 
   DISALLOW_COPY_AND_ASSIGN(VRManagerChild);
 };
 
 } // namespace mozilla
 } // namespace gfx
 
 #endif // MOZILLA_GFX_VR_VRMANAGERCHILD_H
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -3,16 +3,17 @@
 /* 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/ArrayUtils.h"
 #include "mozilla/GfxMessageUtils.h"
 #include "VRManager.h"
 
 #include "gfxVR.h"
 
 namespace IPC {
 
 template<>
@@ -104,51 +105,56 @@ struct ParamTraits<mozilla::gfx::VRDispl
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mType);
     WriteParam(aMsg, aParam.mDisplayID);
     WriteParam(aMsg, aParam.mPresentingGroups);
     WriteParam(aMsg, aParam.mGroupMask);
     WriteParam(aMsg, aParam.mFrameId);
     WriteParam(aMsg, aParam.mDisplayState);
-    for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.mLastSensorState); i++) {
       WriteParam(aMsg, aParam.mLastSensorState[i]);
     }
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.mControllerState); i++) {
+      WriteParam(aMsg, aParam.mControllerState[i]);
+    }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->mType)) ||
         !ReadParam(aMsg, aIter, &(aResult->mDisplayID)) ||
         !ReadParam(aMsg, aIter, &(aResult->mPresentingGroups)) ||
         !ReadParam(aMsg, aIter, &(aResult->mGroupMask)) ||
         !ReadParam(aMsg, aIter, &(aResult->mFrameId)) ||
         !ReadParam(aMsg, aIter, &(aResult->mDisplayState))) {
       return false;
     }
-    for (int i = 0; i < mozilla::gfx::kVRMaxLatencyFrames; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->mLastSensorState); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mLastSensorState[i]))) {
         return false;
       }
     }
-
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->mControllerState); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mControllerState[i]))) {
+        return false;
+      }
+    }
     return true;
   }
 };
 
+
 template <>
-struct ParamTraits<mozilla::gfx::VRHMDSensorState>
+struct ParamTraits<mozilla::gfx::VRPose>
 {
-  typedef mozilla::gfx::VRHMDSensorState paramType;
+  typedef mozilla::gfx::VRPose paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.timestamp);
-    WriteParam(aMsg, aParam.inputFrameID);
-    WriteParam(aMsg, aParam.flags);
     WriteParam(aMsg, aParam.orientation[0]);
     WriteParam(aMsg, aParam.orientation[1]);
     WriteParam(aMsg, aParam.orientation[2]);
     WriteParam(aMsg, aParam.orientation[3]);
     WriteParam(aMsg, aParam.position[0]);
     WriteParam(aMsg, aParam.position[1]);
     WriteParam(aMsg, aParam.position[2]);
     WriteParam(aMsg, aParam.angularVelocity[0]);
@@ -158,30 +164,21 @@ struct ParamTraits<mozilla::gfx::VRHMDSe
     WriteParam(aMsg, aParam.angularAcceleration[1]);
     WriteParam(aMsg, aParam.angularAcceleration[2]);
     WriteParam(aMsg, aParam.linearVelocity[0]);
     WriteParam(aMsg, aParam.linearVelocity[1]);
     WriteParam(aMsg, aParam.linearVelocity[2]);
     WriteParam(aMsg, aParam.linearAcceleration[0]);
     WriteParam(aMsg, aParam.linearAcceleration[1]);
     WriteParam(aMsg, aParam.linearAcceleration[2]);
-    for (int i=0; i < 16; i++) {
-      WriteParam(aMsg, aParam.leftViewMatrix[i]);
-    }
-    for (int i=0; i < 16; i++) {
-      WriteParam(aMsg, aParam.rightViewMatrix[i]);
-    }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
-    if (!ReadParam(aMsg, aIter, &(aResult->timestamp)) ||
-        !ReadParam(aMsg, aIter, &(aResult->inputFrameID)) ||
-        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
-        !ReadParam(aMsg, aIter, &(aResult->orientation[0])) ||
+    if (!ReadParam(aMsg, aIter, &(aResult->orientation[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->orientation[3])) ||
         !ReadParam(aMsg, aIter, &(aResult->position[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->position[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->position[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->angularVelocity[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->angularVelocity[1])) ||
@@ -192,22 +189,53 @@ struct ParamTraits<mozilla::gfx::VRHMDSe
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearVelocity[2])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[0])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[1])) ||
         !ReadParam(aMsg, aIter, &(aResult->linearAcceleration[2]))) {
       return false;
     }
-    for (int i=0; i < 16; i++) {
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::gfx::VRHMDSensorState>
+{
+  typedef mozilla::gfx::VRHMDSensorState paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.timestamp);
+    WriteParam(aMsg, aParam.inputFrameID);
+    WriteParam(aMsg, aParam.flags);
+    WriteParam(aMsg, aParam.pose);
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.leftViewMatrix); i++) {
+      WriteParam(aMsg, aParam.leftViewMatrix[i]);
+    }
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.rightViewMatrix); i++) {
+      WriteParam(aMsg, aParam.rightViewMatrix[i]);
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->timestamp)) ||
+        !ReadParam(aMsg, aIter, &(aResult->inputFrameID)) ||
+        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
+        !ReadParam(aMsg, aIter, &(aResult->pose))) {
+      return false;
+    }
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->leftViewMatrix); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->leftViewMatrix[i]))) {
         return false;
       }
     }
-    for (int i=0; i < 16; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->rightViewMatrix); i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->rightViewMatrix[i]))) {
         return false;
       }
     }
     return true;
   }
 };
 
@@ -241,55 +269,63 @@ struct ParamTraits<mozilla::gfx::VRField
 template <>
 struct ParamTraits<mozilla::gfx::VRControllerState>
 {
   typedef mozilla::gfx::VRControllerState paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     nsCString controllerName;
-    controllerName.Assign(aParam.mControllerName); // FINDME!! HACK! - Bounds checking?
+    controllerName.Assign(aParam.controllerName); // FINDME!! HACK! - Bounds checking?
     WriteParam(aMsg, controllerName);
-    WriteParam(aMsg, aParam.mNumButtons);
-    WriteParam(aMsg, aParam.mNumAxes);
-    WriteParam(aMsg, aParam.mNumTriggers);
-    WriteParam(aMsg, aParam.mNumHaptics);
-    WriteParam(aMsg, aParam.mButtonPressed);
-    WriteParam(aMsg, aParam.mButtonTouched);
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
-      WriteParam(aMsg, aParam.mAxisValue[i]);
+    WriteParam(aMsg, aParam.hand);
+    WriteParam(aMsg, aParam.numButtons);
+    WriteParam(aMsg, aParam.numAxes);
+    WriteParam(aMsg, aParam.numHaptics);
+    WriteParam(aMsg, aParam.buttonPressed);
+    WriteParam(aMsg, aParam.buttonTouched);
+    WriteParam(aMsg, aParam.flags);
+    WriteParam(aMsg, aParam.pose);
+    WriteParam(aMsg, aParam.isPositionValid);
+    WriteParam(aMsg, aParam.isOrientationValid);
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.axisValue); i++) {
+      WriteParam(aMsg, aParam.axisValue[i]);
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
-      WriteParam(aMsg, aParam.mTriggerValue[i]);
+    for (size_t i = 0; i < mozilla::ArrayLength(aParam.triggerValue); i++) {
+      WriteParam(aMsg, aParam.triggerValue[i]);
     }
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     nsCString controllerName;
     if (!ReadParam(aMsg, aIter, &(controllerName)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumButtons)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumAxes)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumTriggers)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mNumHaptics)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mButtonPressed)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mButtonTouched))) {
+        !ReadParam(aMsg, aIter, &(aResult->hand)) ||
+        !ReadParam(aMsg, aIter, &(aResult->numButtons)) ||
+        !ReadParam(aMsg, aIter, &(aResult->numAxes)) ||
+        !ReadParam(aMsg, aIter, &(aResult->numHaptics)) ||
+        !ReadParam(aMsg, aIter, &(aResult->buttonPressed)) ||
+        !ReadParam(aMsg, aIter, &(aResult->buttonTouched)) ||
+        !ReadParam(aMsg, aIter, &(aResult->flags)) ||
+        !ReadParam(aMsg, aIter, &(aResult->pose)) ||
+        !ReadParam(aMsg, aIter, &(aResult->isPositionValid)) ||
+        !ReadParam(aMsg, aIter, &(aResult->isOrientationValid))) {
       return false;
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxAxis; i++) {
-      if (!ReadParam(aMsg, aIter, &(aResult->mAxisValue[i]))) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->axisValue); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->axisValue[i]))) {
         return false;
       }
     }
-    for (int i=0; i < mozilla::gfx::kVRControllerMaxTriggers; i++) {
-      if (!ReadParam(aMsg, aIter, &(aResult->mTriggerValue[i]))) {
+    for (size_t i = 0; i < mozilla::ArrayLength(aResult->triggerValue); i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->triggerValue[i]))) {
         return false;
       }
     }
-    strncpy(aResult->mControllerName, controllerName.BeginReading(), mozilla::gfx::kVRControllerNameMaxLen); // FINDME! TODO! HACK!  Safe? Better way?
+    strncpy(aResult->controllerName, controllerName.BeginReading(), mozilla::gfx::kVRControllerNameMaxLen); // FINDME! TODO! HACK!  Safe? Better way?
 
     return true;
   }
 };
 
 template <>
 struct ParamTraits<mozilla::gfx::VRControllerInfo>
 {
--- a/gfx/vr/service/OpenVRSession.cpp
+++ b/gfx/vr/service/OpenVRSession.cpp
@@ -1,55 +1,130 @@
 #include "OpenVRSession.h"
+#include "gfxPrefs.h"
 
 #if defined(XP_WIN)
 #include <d3d11.h>
 #include "mozilla/gfx/DeviceManagerDx.h"
 #endif // defined(XP_WIN)
 
-#if defined(MOZILLA_INTERNAL_API)
 #include "mozilla/dom/GamepadEventTypes.h"
 #include "mozilla/dom/GamepadBinding.h"
-#endif
 
 #if !defined(M_PI)
 #define M_PI 3.14159265358979323846264338327950288
 #endif
 
 #define BTN_MASK_FROM_ID(_id) \
   ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
 
+static const uint32_t kNumOpenVRHaptcs = 1;
+
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gfx {
 
+namespace {
+
+dom::GamepadHand
+GetControllerHandFromControllerRole(::vr::ETrackedControllerRole aRole)
+{
+  dom::GamepadHand hand;
+
+  switch(aRole) {
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_OptOut:
+      hand = dom::GamepadHand::_empty;
+      break;
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
+      hand = dom::GamepadHand::Left;
+      break;
+    case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
+      hand = dom::GamepadHand::Right;
+      break;
+    default:
+      hand = dom::GamepadHand::_empty;
+      MOZ_ASSERT(false);
+      break;
+  }
+
+  return hand;
+}
+
+
+void
+UpdateButton(VRControllerState& aState, const ::vr::VRControllerState_t& aControllerState, uint32_t aButtonIndex, uint64_t aButtonMask)
+{
+  uint64_t mask = (1ULL << aButtonIndex);
+  if ((aControllerState.ulButtonPressed & aButtonMask) == 0) {
+    // not pressed
+    aState.buttonPressed &= ~mask;
+    aState.triggerValue[aButtonIndex] = 0.0f;
+  } else {
+    // pressed
+    aState.buttonPressed |= mask;
+    aState.triggerValue[aButtonIndex] = 1.0f;
+  }
+  if ((aControllerState.ulButtonTouched & aButtonMask) == 0) {
+    // not touched
+    aState.buttonTouched &= ~mask;
+  } else {
+    // touched
+    aState.buttonTouched |= mask;
+  }
+}
+
+void
+UpdateTrigger(VRControllerState& aState, uint32_t aButtonIndex, float aValue, float aThreshold)
+{
+  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
+  // We prefer to let developers to set their own threshold for the adjustment.
+  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
+  // we just check the button value is larger than the threshold value or not.
+  uint64_t mask = (1ULL << aButtonIndex);
+  aState.triggerValue[aButtonIndex] = aValue;
+  if (aValue > aThreshold) {
+    aState.buttonPressed |= mask;
+    aState.buttonTouched |= mask;
+  } else {
+    aState.buttonPressed &= ~mask;
+    aState.buttonTouched &= ~mask;
+  }
+}
+
+}; // anonymous namespace
+
 OpenVRSession::OpenVRSession()
   : VRSession()
   , mVRSystem(nullptr)
   , mVRChaperone(nullptr)
   , mVRCompositor(nullptr)
+  , mControllerDeviceIndex{0}
   , mShouldQuit(false)
+  , mIsWindowsMR(false)
 {
 }
 
 OpenVRSession::~OpenVRSession()
 {
   Shutdown();
 }
 
 bool
 OpenVRSession::Initialize(mozilla::gfx::VRSystemState& aSystemState)
 {
   if (mVRSystem != nullptr) {
     // Already initialized
     return true;
   }
+  if (!::vr::VR_IsRuntimeInstalled()) {
+    return false;
+  }
   if (!::vr::VR_IsHmdPresent()) {
-    fprintf(stderr, "No HMD detected, VR_IsHmdPresent returned false.\n");
     return false;
   }
 
   ::vr::HmdError err;
 
   ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
   if (err) {
     return false;
@@ -138,131 +213,131 @@ OpenVRSession::InitState(VRSystemState& 
   }
 
   uint32_t w, h;
   mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
   state.mEyeResolution.width = w;
   state.mEyeResolution.height = h;
 
   // default to an identity quaternion
-  aSystemState.sensorState.orientation[3] = 1.0f;
+  aSystemState.sensorState.pose.orientation[3] = 1.0f;
 
   UpdateStageParameters(state);
-  UpdateEyeParameters(state);
+  UpdateEyeParameters(aSystemState);
 
   VRHMDSensorState& sensorState = aSystemState.sensorState;
   sensorState.flags = (VRDisplayCapabilityFlags)(
     (int)VRDisplayCapabilityFlags::Cap_Orientation |
     (int)VRDisplayCapabilityFlags::Cap_Position);
-  sensorState.orientation[3] = 1.0f; // Default to an identity quaternion
+  sensorState.pose.orientation[3] = 1.0f; // Default to an identity quaternion
 
   return true;
 }
 
 void
-OpenVRSession::UpdateStageParameters(VRDisplayState& state)
+OpenVRSession::UpdateStageParameters(VRDisplayState& aState)
 {
   float sizeX = 0.0f;
   float sizeZ = 0.0f;
   if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
     ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
-    state.mStageSize.width = sizeX;
-    state.mStageSize.height = sizeZ;
+    aState.mStageSize.width = sizeX;
+    aState.mStageSize.height = sizeZ;
 
-    state.mSittingToStandingTransform[0] = t.m[0][0];
-    state.mSittingToStandingTransform[1] = t.m[1][0];
-    state.mSittingToStandingTransform[2] = t.m[2][0];
-    state.mSittingToStandingTransform[3] = 0.0f;
+    aState.mSittingToStandingTransform[0] = t.m[0][0];
+    aState.mSittingToStandingTransform[1] = t.m[1][0];
+    aState.mSittingToStandingTransform[2] = t.m[2][0];
+    aState.mSittingToStandingTransform[3] = 0.0f;
 
-    state.mSittingToStandingTransform[4] = t.m[0][1];
-    state.mSittingToStandingTransform[5] = t.m[1][1];
-    state.mSittingToStandingTransform[6] = t.m[2][1];
-    state.mSittingToStandingTransform[7] = 0.0f;
+    aState.mSittingToStandingTransform[4] = t.m[0][1];
+    aState.mSittingToStandingTransform[5] = t.m[1][1];
+    aState.mSittingToStandingTransform[6] = t.m[2][1];
+    aState.mSittingToStandingTransform[7] = 0.0f;
 
-    state.mSittingToStandingTransform[8] = t.m[0][2];
-    state.mSittingToStandingTransform[9] = t.m[1][2];
-    state.mSittingToStandingTransform[10] = t.m[2][2];
-    state.mSittingToStandingTransform[11] = 0.0f;
+    aState.mSittingToStandingTransform[8] = t.m[0][2];
+    aState.mSittingToStandingTransform[9] = t.m[1][2];
+    aState.mSittingToStandingTransform[10] = t.m[2][2];
+    aState.mSittingToStandingTransform[11] = 0.0f;
 
-    state.mSittingToStandingTransform[12] = t.m[0][3];
-    state.mSittingToStandingTransform[13] = t.m[1][3];
-    state.mSittingToStandingTransform[14] = t.m[2][3];
-    state.mSittingToStandingTransform[15] = 1.0f;
+    aState.mSittingToStandingTransform[12] = t.m[0][3];
+    aState.mSittingToStandingTransform[13] = t.m[1][3];
+    aState.mSittingToStandingTransform[14] = t.m[2][3];
+    aState.mSittingToStandingTransform[15] = 1.0f;
   } else {
     // If we fail, fall back to reasonable defaults.
     // 1m x 1m space, 0.75m high in seated position
-
-    state.mStageSize.width = 1.0f;
-    state.mStageSize.height = 1.0f;
+    aState.mStageSize.width = 1.0f;
+    aState.mStageSize.height = 1.0f;
 
-    state.mSittingToStandingTransform[0] = 1.0f;
-    state.mSittingToStandingTransform[1] = 0.0f;
-    state.mSittingToStandingTransform[2] = 0.0f;
-    state.mSittingToStandingTransform[3] = 0.0f;
+    aState.mSittingToStandingTransform[0] = 1.0f;
+    aState.mSittingToStandingTransform[1] = 0.0f;
+    aState.mSittingToStandingTransform[2] = 0.0f;
+    aState.mSittingToStandingTransform[3] = 0.0f;
 
-    state.mSittingToStandingTransform[4] = 0.0f;
-    state.mSittingToStandingTransform[5] = 1.0f;
-    state.mSittingToStandingTransform[6] = 0.0f;
-    state.mSittingToStandingTransform[7] = 0.0f;
+    aState.mSittingToStandingTransform[4] = 0.0f;
+    aState.mSittingToStandingTransform[5] = 1.0f;
+    aState.mSittingToStandingTransform[6] = 0.0f;
+    aState.mSittingToStandingTransform[7] = 0.0f;
 
-    state.mSittingToStandingTransform[8] = 0.0f;
-    state.mSittingToStandingTransform[9] = 0.0f;
-    state.mSittingToStandingTransform[10] = 1.0f;
-    state.mSittingToStandingTransform[11] = 0.0f;
+    aState.mSittingToStandingTransform[8] = 0.0f;
+    aState.mSittingToStandingTransform[9] = 0.0f;
+    aState.mSittingToStandingTransform[10] = 1.0f;
+    aState.mSittingToStandingTransform[11] = 0.0f;
 
-    state.mSittingToStandingTransform[12] = 0.0f;
-    state.mSittingToStandingTransform[13] = 0.75f;
-    state.mSittingToStandingTransform[14] = 0.0f;
-    state.mSittingToStandingTransform[15] = 1.0f;
+    aState.mSittingToStandingTransform[12] = 0.0f;
+    aState.mSittingToStandingTransform[13] = 0.75f;
+    aState.mSittingToStandingTransform[14] = 0.0f;
+    aState.mSittingToStandingTransform[15] = 1.0f;
   }
 }
 
 void
-OpenVRSession::UpdateEyeParameters(VRDisplayState& state, gfx::Matrix4x4* headToEyeTransforms /* = nullptr */)
+OpenVRSession::UpdateEyeParameters(VRSystemState& aState)
 {
+  // This must be called every frame in order to
+  // account for continuous adjustments to ipd.
+  gfx::Matrix4x4 headToEyeTransforms[2];
+
   for (uint32_t eye = 0; eye < 2; ++eye) {
     ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
-    state.mEyeTranslation[eye].x = eyeToHead.m[0][3];
-    state.mEyeTranslation[eye].y = eyeToHead.m[1][3];
-    state.mEyeTranslation[eye].z = eyeToHead.m[2][3];
+    aState.displayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
+    aState.displayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
+    aState.displayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
 
     float left, right, up, down;
     mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &left, &right, &up, &down);
-    state.mEyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
-    state.mEyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
-    state.mEyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
-    state.mEyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].upDegrees = atan(-up) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].rightDegrees = atan(right) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].downDegrees = atan(down) * 180.0 / M_PI;
+    aState.displayState.mEyeFOV[eye].leftDegrees = atan(-left) * 180.0 / M_PI;
 
-    if (headToEyeTransforms) {
-      Matrix4x4 pose;
-      // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
-      // because of its arrangement, we can copy the 12 elements in and
-      // then transpose them to the right place.
-      memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
-      pose.Transpose();
-      pose.Invert();
-      headToEyeTransforms[eye] = pose;
-    }
+    Matrix4x4 pose;
+    // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
+    // because of its arrangement, we can copy the 12 elements in and
+    // then transpose them to the right place.
+    memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
+    pose.Transpose();
+    pose.Invert();
+    headToEyeTransforms[eye] = pose;
   }
+  aState.sensorState.CalcViewMatrices(headToEyeTransforms);
 }
 
 void
-OpenVRSession::GetSensorState(VRSystemState& state)
+OpenVRSession::UpdateHeadsetPose(VRSystemState& aState)
 {
   const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
   ::vr::TrackedDevicePose_t poses[posesSize];
   // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
   mVRCompositor->WaitGetPoses(nullptr, 0, poses, posesSize);
-  gfx::Matrix4x4 headToEyeTransforms[2];
-  UpdateEyeParameters(state.displayState, headToEyeTransforms);
 
   ::vr::Compositor_FrameTiming timing;
   timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
   if (mVRCompositor->GetFrameTiming(&timing)) {
-    state.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
+    aState.sensorState.timestamp = timing.m_flSystemTimeInSeconds;
   } else {
     // This should not happen, but log it just in case
     fprintf(stderr, "OpenVR - IVRCompositor::GetFrameTiming failed");
   }
 
   if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
     poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
     poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
@@ -276,49 +351,421 @@ OpenVRSession::GetSensorState(VRSystemSt
     // pull out a Quaternion.
     memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
     m.Transpose();
 
     gfx::Quaternion rot;
     rot.SetFromRotationMatrix(m);
     rot.Invert();
 
-    state.sensorState.flags = (VRDisplayCapabilityFlags)((int)state.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Orientation);
-    state.sensorState.orientation[0] = rot.x;
-    state.sensorState.orientation[1] = rot.y;
-    state.sensorState.orientation[2] = rot.z;
-    state.sensorState.orientation[3] = rot.w;
-    state.sensorState.angularVelocity[0] = pose.vAngularVelocity.v[0];
-    state.sensorState.angularVelocity[1] = pose.vAngularVelocity.v[1];
-    state.sensorState.angularVelocity[2] = pose.vAngularVelocity.v[2];
+    aState.sensorState.flags = (VRDisplayCapabilityFlags)((int)aState.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Orientation);
+    aState.sensorState.pose.orientation[0] = rot.x;
+    aState.sensorState.pose.orientation[1] = rot.y;
+    aState.sensorState.pose.orientation[2] = rot.z;
+    aState.sensorState.pose.orientation[3] = rot.w;
+    aState.sensorState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+    aState.sensorState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+    aState.sensorState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
+
+    aState.sensorState.flags =(VRDisplayCapabilityFlags)((int)aState.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Position);
+    aState.sensorState.pose.position[0] = m._41;
+    aState.sensorState.pose.position[1] = m._42;
+    aState.sensorState.pose.position[2] = m._43;
+    aState.sensorState.pose.linearVelocity[0] = pose.vVelocity.v[0];
+    aState.sensorState.pose.linearVelocity[1] = pose.vVelocity.v[1];
+    aState.sensorState.pose.linearVelocity[2] = pose.vVelocity.v[2];
+  }
+}
+
+void
+OpenVRSession::EnumerateControllers(VRSystemState& aState)
+{
+  MOZ_ASSERT(mVRSystem);
+
+  bool controllerPresent[kVRControllerMaxCount] = { false };
+
+  // Basically, we would have HMDs in the tracked devices,
+  // but we are just interested in the controllers.
+  for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
+       trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
+
+    if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
+      continue;
+    }
+
+    const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
+                                                 GetTrackedDeviceClass(trackedDevice);
+    if (deviceType != ::vr::TrackedDeviceClass_Controller
+        && deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
+      continue;
+    }
+
+    uint32_t stateIndex = 0;
+    uint32_t firstEmptyIndex = kVRControllerMaxCount;
+
+    // Find the existing controller
+    for (stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+      if (mControllerDeviceIndex[stateIndex] == 0 && firstEmptyIndex == kVRControllerMaxCount) {
+        firstEmptyIndex = stateIndex;
+      }
+      if (mControllerDeviceIndex[stateIndex] == trackedDevice) {
+        break;
+      }
+    }
+    if (stateIndex == kVRControllerMaxCount) {
+      // This is a new controller, let's add it
+      if (firstEmptyIndex == kVRControllerMaxCount) {
+        NS_WARNING("OpenVR - Too many controllers, need to increase kVRControllerMaxCount.");
+        continue;
+      }
+      stateIndex = firstEmptyIndex;
+      mControllerDeviceIndex[stateIndex] = trackedDevice;
+      VRControllerState& controllerState = aState.controllerState[stateIndex];
+      uint32_t numButtons = 0;
+      uint32_t numAxes = 0;
 
-    state.sensorState.flags =(VRDisplayCapabilityFlags)((int)state.sensorState.flags | (int)VRDisplayCapabilityFlags::Cap_Position);
-    state.sensorState.position[0] = m._41;
-    state.sensorState.position[1] = m._42;
-    state.sensorState.position[2] = m._43;
-    state.sensorState.linearVelocity[0] = pose.vVelocity.v[0];
-    state.sensorState.linearVelocity[1] = pose.vVelocity.v[1];
-    state.sensorState.linearVelocity[2] = pose.vVelocity.v[2];
+      // Scan the axes that the controllers support
+      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
+        const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
+                                      static_cast<vr::TrackedDeviceProperty>(
+                                      ::vr::Prop_Axis0Type_Int32 + j));
+        switch (supportAxis) {
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
+            numAxes += 2; // It has x and y axes.
+            ++numButtons;
+            break;
+          case ::vr::k_eControllerAxis_Trigger:
+            if (j <= 2) {
+              ++numButtons;
+            } else {
+          #ifdef DEBUG
+              // SteamVR Knuckles is the only special case for using 2D axis values on triggers.
+              ::vr::ETrackedPropertyError err;
+              uint32_t requiredBufferLen;
+              char charBuf[128];
+              requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(trackedDevice,
+                                  ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
+              MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+              nsCString deviceId(charBuf);
+              MOZ_ASSERT(deviceId.Find("knuckles") != kNotFound);
+          #endif // #ifdef DEBUG
+              numButtons += 2;
+            }
+            break;
+        }
+      }
+
+      // Scan the buttons that the controllers support
+      const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
+                                       trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_A)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_Grip)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
+        ++numButtons;
+      }
+      if (supportButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
+        ++numButtons;
+      }
+
+      nsCString deviceId;
+      GetControllerDeviceId(deviceType, trackedDevice, deviceId);
+
+      strncpy(controllerState.controllerName, deviceId.BeginReading(), kVRControllerNameMaxLen);
+      controllerState.numButtons = numButtons;
+      controllerState.numAxes = numAxes;
+      controllerState.numHaptics = kNumOpenVRHaptcs;
+
+      // If the Windows MR controller doesn't has the amount
+      // of buttons or axes as our expectation, switching off
+      // the workaround for Windows MR.
+      if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
+        mIsWindowsMR = false;
+        NS_WARNING("OpenVR - Switching off Windows MR mode.");
+      }
+    }
+    controllerPresent[stateIndex] = true;
   }
-
-  state.sensorState.CalcViewMatrices(headToEyeTransforms);
-  state.sensorState.inputFrameID++;
+  // Clear out entries for disconnected controllers
+  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    if (!controllerPresent[stateIndex] && mControllerDeviceIndex[stateIndex] != 0) {
+      mControllerDeviceIndex[stateIndex] = 0;
+      memset(&aState.controllerState[stateIndex], 0, sizeof(VRControllerState));
+    }
+  }
 }
 
 void
-OpenVRSession::GetControllerState(VRSystemState &state)
+OpenVRSession::UpdateControllerButtons(VRSystemState& aState)
 {
-  // TODO - Implement
+  MOZ_ASSERT(mVRSystem);
+
+  // Compared to Edge, we have a wrong implementation for the vertical axis value.
+  // In order to not affect the current VR content, we add a workaround for yAxis.
+  const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
+  const float triggerThreshold = gfxPrefs::VRControllerTriggerThreshold();
+
+  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    ::vr::TrackedDeviceIndex_t trackedDevice = mControllerDeviceIndex[stateIndex];
+    if (trackedDevice == 0) {
+      continue;
+    }
+    VRControllerState& controllerState = aState.controllerState[stateIndex];
+    const ::vr::ETrackedControllerRole role = mVRSystem->
+                                          GetControllerRoleForTrackedDeviceIndex(
+                                          trackedDevice);
+    dom::GamepadHand hand = GetControllerHandFromControllerRole(role);
+    controllerState.hand = hand;
+
+    ::vr::VRControllerState_t vrControllerState;
+    if (mVRSystem->GetControllerState(trackedDevice, &vrControllerState, sizeof(vrControllerState))) {
+      uint32_t axisIdx = 0;
+      uint32_t buttonIdx = 0;
+      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
+        const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
+                                   trackedDevice,
+                                   static_cast<::vr::TrackedDeviceProperty>(
+                                   ::vr::Prop_Axis0Type_Int32 + j));
+        switch (axisType) {
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
+          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
+          {
+            if (mIsWindowsMR) {
+              // Adjust the input mapping for Windows MR which has
+              // different order.
+              axisIdx = (axisIdx == 0) ? 2 : 0;
+              buttonIdx = (buttonIdx == 0) ? 4 : 0;
+            }
+
+            controllerState.axisValue[axisIdx] = vrControllerState.rAxis[j].x;
+            ++axisIdx;
+            controllerState.axisValue[axisIdx] = vrControllerState.rAxis[j].y * yAxisInvert;
+            ++axisIdx;
+            uint64_t buttonMask = ::vr::ButtonMaskFromId(
+                                 static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j));
+
+            UpdateButton(controllerState, vrControllerState, buttonIdx, buttonMask);
+            ++buttonIdx;
+
+            if (mIsWindowsMR) {
+              axisIdx = (axisIdx == 4) ? 2 : 4;
+              buttonIdx = (buttonIdx == 5) ? 1 : 2;
+            }
+            break;
+          }
+          case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
+          {
+            if (j <= 2) {
+              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].x, triggerThreshold);
+              ++buttonIdx;
+            } else {
+              // For SteamVR Knuckles.
+              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].x, triggerThreshold);
+              ++buttonIdx;
+              UpdateTrigger(controllerState, buttonIdx, vrControllerState.rAxis[j].y, triggerThreshold);
+              ++buttonIdx;
+            }
+            break;
+          }
+        }
+      }
+
+      const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
+                                         trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_A)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_A));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_Grip)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_Grip));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_ApplicationMenu));
+        ++buttonIdx;
+      }
+      if (mIsWindowsMR) {
+        // button 4 in Windows MR has already been assigned
+        // to k_eControllerAxis_TrackPad.
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Left));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Up));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Right));
+        ++buttonIdx;
+      }
+      if (supportedButtons &
+          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
+        UpdateButton(controllerState, vrControllerState, buttonIdx, BTN_MASK_FROM_ID(k_EButton_DPad_Down));
+        ++buttonIdx;
+      }
+    }
+  }
+}
+
+void
+OpenVRSession::UpdateControllerPoses(VRSystemState& aState)
+{
+  MOZ_ASSERT(mVRSystem);
+
+  ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
+  mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
+                                             poses, ::vr::k_unMaxTrackedDeviceCount);
+
+  for (uint32_t stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
+    ::vr::TrackedDeviceIndex_t trackedDevice = mControllerDeviceIndex[stateIndex];
+    if (trackedDevice == 0) {
+      continue;
+    }
+    VRControllerState& controllerState = aState.controllerState[stateIndex];
+    const ::vr::TrackedDevicePose_t& pose = poses[trackedDevice];
+
+    if (pose.bDeviceIsConnected) {
+      controllerState.flags = (dom::GamepadCapabilityFlags::Cap_Orientation |
+                               dom::GamepadCapabilityFlags::Cap_Position);
+    } else {
+      controllerState.flags =  dom::GamepadCapabilityFlags::Cap_None;
+    }
+    if (pose.bPoseIsValid &&
+        pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
+      gfx::Matrix4x4 m;
+
+      // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
+      // because of its arrangement, we can copy the 12 elements in and
+      // then transpose them to the right place.  We do this so we can
+      // pull out a Quaternion.
+      memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
+      m.Transpose();
+
+      gfx::Quaternion rot;
+      rot.SetFromRotationMatrix(m);
+      rot.Invert();
+
+      controllerState.pose.orientation[0] = rot.x;
+      controllerState.pose.orientation[1] = rot.y;
+      controllerState.pose.orientation[2] = rot.z;
+      controllerState.pose.orientation[3] = rot.w;
+      controllerState.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
+      controllerState.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
+      controllerState.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
+      controllerState.pose.angularAcceleration[0] = 0.0f;
+      controllerState.pose.angularAcceleration[1] = 0.0f;
+      controllerState.pose.angularAcceleration[2] = 0.0f;
+      controllerState.isOrientationValid = true;
+
+      controllerState.pose.position[0] = m._41;
+      controllerState.pose.position[1] = m._42;
+      controllerState.pose.position[2] = m._43;
+      controllerState.pose.linearVelocity[0] = pose.vVelocity.v[0];
+      controllerState.pose.linearVelocity[1] = pose.vVelocity.v[1];
+      controllerState.pose.linearVelocity[2] = pose.vVelocity.v[2];
+      controllerState.pose.linearAcceleration[0] = 0.0f;
+      controllerState.pose.linearAcceleration[1] = 0.0f;
+      controllerState.pose.linearAcceleration[2] = 0.0f;
+      controllerState.isPositionValid = true;
+    } else {
+      controllerState.isOrientationValid = false;
+      controllerState.isPositionValid = false;
+    }
+  }
+}
+
+void
+OpenVRSession::GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
+                                     ::vr::TrackedDeviceIndex_t aDeviceIndex,
+                                     nsCString& aId)
+{
+  switch (aDeviceType) {
+    case ::vr::TrackedDeviceClass_Controller:
+    {
+      ::vr::ETrackedPropertyError err;
+      uint32_t requiredBufferLen;
+      bool isFound = false;
+      char charBuf[128];
+      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
+                          ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
+      if (requiredBufferLen > 128) {
+        MOZ_CRASH("Larger than the buffer size.");
+      }
+      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+      nsCString deviceId(charBuf);
+      if (deviceId.Find("knuckles") != kNotFound) {
+        aId.AssignLiteral("OpenVR Knuckles");
+        isFound = true;
+      }
+      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
+        ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
+      if (requiredBufferLen > 128) {
+        MOZ_CRASH("Larger than the buffer size.");
+      }
+      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
+      deviceId.Assign(charBuf);
+      if (deviceId.Find("MRSOURCE") != kNotFound) {
+        aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
+        mIsWindowsMR = true;
+        isFound = true;
+      }
+      if (!isFound) {
+        aId.AssignLiteral("OpenVR Gamepad");
+      }
+      break;
+    }
+    case ::vr::TrackedDeviceClass_GenericTracker:
+    {
+      aId.AssignLiteral("OpenVR Tracker");
+      break;
+    }
+    default:
+      MOZ_ASSERT(false);
+      break;
+  }
 }
 
 void
 OpenVRSession::StartFrame(mozilla::gfx::VRSystemState& aSystemState)
 {
-  GetSensorState(aSystemState);
-  GetControllerState(aSystemState);
+  UpdateHeadsetPose(aSystemState);
+  UpdateEyeParameters(aSystemState);
+  EnumerateControllers(aSystemState);
+  UpdateControllerButtons(aSystemState);
+  UpdateControllerPoses(aSystemState);
+  aSystemState.sensorState.inputFrameID++;
 }
 
 bool
 OpenVRSession::ShouldQuit() const
 {
   return mShouldQuit;
 }
 
--- a/gfx/vr/service/OpenVRSession.h
+++ b/gfx/vr/service/OpenVRSession.h
@@ -37,29 +37,36 @@ public:
   void StopPresentation() override;
   bool SubmitFrame(const mozilla::gfx::VRLayer_Stereo_Immersive& aLayer) override;
 
 private:
   // OpenVR State
   ::vr::IVRSystem* mVRSystem = nullptr;
   ::vr::IVRChaperone* mVRChaperone = nullptr;
   ::vr::IVRCompositor* mVRCompositor = nullptr;
+  ::vr::TrackedDeviceIndex_t mControllerDeviceIndex[kVRControllerMaxCount];
   bool mShouldQuit;
+  bool mIsWindowsMR;
 
   bool InitState(mozilla::gfx::VRSystemState& aSystemState);
-  void UpdateStageParameters(mozilla::gfx::VRDisplayState& state);
-  void UpdateEyeParameters(mozilla::gfx::VRDisplayState& state, gfx::Matrix4x4* headToEyeTransforms = nullptr);
-  void GetSensorState(mozilla::gfx::VRSystemState& state);
-  void GetControllerState(VRSystemState &state);
+  void UpdateStageParameters(mozilla::gfx::VRDisplayState& aState);
+  void UpdateEyeParameters(mozilla::gfx::VRSystemState& aState);
+  void UpdateHeadsetPose(mozilla::gfx::VRSystemState& aState);
+  void EnumerateControllers(VRSystemState& aState);
+  void UpdateControllerPoses(VRSystemState& aState);
+  void UpdateControllerButtons(VRSystemState& aState);
 
   bool SubmitFrame(void* aTextureHandle,
                    ::vr::ETextureType aTextureType,
                    const VRLayerEyeRect& aLeftEyeRect,
                    const VRLayerEyeRect& aRightEyeRect);
 #if defined(XP_WIN)
   bool CreateD3DObjects();
 #endif
+  void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
+                             ::vr::TrackedDeviceIndex_t aDeviceIndex,
+                             nsCString& aId);
 };
 
 } // namespace mozilla
 } // namespace gfx
 
 #endif // GFX_VR_SERVICE_OPENVRSESSION_H
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
@@ -204,16 +204,31 @@ import org.mozilla.gecko.annotation.Wrap
                     Log.e(LOGTAG, "Failed to finalize SurfaceTexture", t);
                 }
             } catch (Exception e) {
                 Log.e(LOGTAG, "Failed to destroy SurfaceTexture", e);
             }
         }
     }
 
+    @WrapForJNI
+    public static void detachAllFromGLContext(long context) {
+        synchronized (sSurfaceTextures) {
+            for (GeckoSurfaceTexture tex : sSurfaceTextures.values()) {
+                try {
+                    if (tex.isAttachedToGLContext(context)) {
+                        tex.detachFromGLContext();
+                    }
+                } catch (Exception e) {
+                    Log.e(LOGTAG, "Failed to detach SurfaceTexture with handle: " + tex.mHandle, e);
+                }
+            }
+        }
+    }
+
     public static GeckoSurfaceTexture acquire(boolean singleBufferMode) {
         if (singleBufferMode && !isSingleBufferSupported()) {
             throw new IllegalArgumentException("single buffer mode not supported on API version < 19");
         }
 
         synchronized (sSurfaceTextures) {
             // We want to limit the maximum number of SurfaceTextures at any one time.
             // This is because they use a large number of fds, and once the process' limit
--- a/mozglue/build/Authenticode.cpp
+++ b/mozglue/build/Authenticode.cpp
@@ -11,26 +11,41 @@
 // See mozmemory_wrap.h for more details. This file is part of libmozglue, so
 // it needs to use _impl suffixes.
 #define MALLOC_DECL(name, return_type, ...) \
   extern "C" MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__);
 #include "malloc_decls.h"
 #include "mozilla/mozalloc.h"
 #endif
 
+// We need Windows 8 functions and structures to be able to verify SHA-256.
+#if defined(_WIN32_WINNT)
+#undef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_WIN8
+#endif // defined(_WIN32_WINNT)
+#if defined(NTDDI_VERSION)
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_WIN8
+#endif // defined(NTDDI_VERSION)
+
 #include "Authenticode.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DynamicallyLinkedFunctionPtr.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsWindowsHelpers.h"
 
 #include <windows.h>
 #include <softpub.h>
 #include <wincrypt.h>
 #include <wintrust.h>
+#include <mscat.h>
 
 namespace {
 
 struct CertStoreDeleter
 {
   typedef HCERTSTORE pointer;
   void operator()(pointer aStore)
   {
@@ -50,23 +65,43 @@ struct CryptMsgDeleter
 struct CertContextDeleter
 {
   void operator()(PCCERT_CONTEXT aCertContext)
   {
     ::CertFreeCertificateContext(aCertContext);
   }
 };
 
+struct CATAdminContextDeleter
+{
+  typedef HCATADMIN pointer;
+  void operator()(pointer aCtx)
+  {
+    static const mozilla::DynamicallyLinkedFunctionPtr<
+      decltype(&::CryptCATAdminReleaseContext)>
+        pCryptCATAdminReleaseContext(L"wintrust.dll",
+                                     "CryptCATAdminReleaseContext");
+
+    MOZ_ASSERT(!!pCryptCATAdminReleaseContext);
+    if (!pCryptCATAdminReleaseContext) {
+      return;
+    }
+
+    pCryptCATAdminReleaseContext(aCtx, 0);
+  }
+};
+
 typedef mozilla::UniquePtr<HCERTSTORE, CertStoreDeleter> CertStoreUniquePtr;
 typedef mozilla::UniquePtr<HCRYPTMSG, CryptMsgDeleter> CryptMsgUniquePtr;
 typedef mozilla::UniquePtr<const CERT_CONTEXT, CertContextDeleter> CertContextUniquePtr;
+typedef mozilla::UniquePtr<HCATADMIN, CATAdminContextDeleter> CATAdminContextUniquePtr;
 
 static const DWORD kEncodingTypes = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
 
-class SignedBinary
+class SignedBinary final
 {
 public:
   explicit SignedBinary(const wchar_t* aFilePath);
 
   explicit operator bool() const
   {
     return mCertStore && mCryptMsg && mCertCtx;
   }
@@ -75,44 +110,41 @@ public:
 
   SignedBinary(const SignedBinary&) = delete;
   SignedBinary(SignedBinary&&) = delete;
   SignedBinary& operator=(const SignedBinary&) = delete;
   SignedBinary& operator=(SignedBinary&&) = delete;
 
 private:
   bool VerifySignature(const wchar_t* aFilePath);
+  bool QueryObject(const wchar_t* aFilePath);
+  static bool VerifySignatureInternal(WINTRUST_DATA& aTrustData);
 
 private:
+  enum class TrustSource
+  {
+    eNone,
+    eEmbedded,
+    eCatalog
+  };
+
+private:
+  TrustSource           mTrustSource;
   CertStoreUniquePtr    mCertStore;
   CryptMsgUniquePtr     mCryptMsg;
   CertContextUniquePtr  mCertCtx;
 };
 
 SignedBinary::SignedBinary(const wchar_t* aFilePath)
+  : mTrustSource(TrustSource::eNone)
 {
   if (!VerifySignature(aFilePath)) {
     return;
   }
 
-  DWORD encodingType, contentType, formatType;
-  HCERTSTORE rawCertStore;
-  HCRYPTMSG rawCryptMsg;
-  BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE, aFilePath,
-                                 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
-                                 CERT_QUERY_FORMAT_FLAG_BINARY, 0,
-                                 &encodingType, &contentType, &formatType,
-                                 &rawCertStore, &rawCryptMsg, nullptr);
-  if (!result) {
-    return;
-  }
-
-  mCertStore.reset(rawCertStore);
-  mCryptMsg.reset(rawCryptMsg);
-
   DWORD certInfoLen = 0;
   BOOL ok = CryptMsgGetParam(mCryptMsg.get(), CMSG_SIGNER_CERT_INFO_PARAM, 0,
                              nullptr, &certInfoLen);
   if (!ok) {
     return;
   }
 
   auto certInfoBuf = mozilla::MakeUnique<char[]>(certInfoLen);
@@ -132,37 +164,221 @@ SignedBinary::SignedBinary(const wchar_t
   if (!certCtx) {
     return;
   }
 
   mCertCtx.reset(certCtx);
 }
 
 bool
+SignedBinary::QueryObject(const wchar_t* aFilePath)
+{
+  DWORD encodingType, contentType, formatType;
+  HCERTSTORE rawCertStore;
+  HCRYPTMSG rawCryptMsg;
+  BOOL result = ::CryptQueryObject(CERT_QUERY_OBJECT_FILE, aFilePath,
+                                   CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+                                   CERT_QUERY_FORMAT_FLAG_BINARY, 0,
+                                   &encodingType, &contentType, &formatType,
+                                   &rawCertStore, &rawCryptMsg, nullptr);
+  if (!result) {
+    return false;
+  }
+
+  mCertStore.reset(rawCertStore);
+  mCryptMsg.reset(rawCryptMsg);
+
+  return true;
+}
+
+/**
+ * @param aTrustData must be a WINTRUST_DATA structure that has been zeroed out
+ *                   and then populated at least with its |cbStruct|,
+ *                   |dwUnionChoice|, and appropriate union field. This function
+ *                   will then populate the remaining fields as appropriate.
+ */
+/* static */ bool
+SignedBinary::VerifySignatureInternal(WINTRUST_DATA& aTrustData)
+{
+  aTrustData.dwUIChoice = WTD_UI_NONE;
+  aTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
+  aTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
+  aTrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+
+  const HWND hwnd = (HWND) INVALID_HANDLE_VALUE;
+  GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+  LONG result = ::WinVerifyTrust(hwnd, &policyGUID, &aTrustData);
+
+  aTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
+  ::WinVerifyTrust(hwnd, &policyGUID, &aTrustData);
+
+  return result == ERROR_SUCCESS;
+}
+
+bool
 SignedBinary::VerifySignature(const wchar_t* aFilePath)
 {
+  // First, try the binary itself
   WINTRUST_FILE_INFO fileInfo = {sizeof(fileInfo)};
   fileInfo.pcwszFilePath = aFilePath;
 
   WINTRUST_DATA trustData = {sizeof(trustData)};
-  trustData.dwUIChoice = WTD_UI_NONE;
-  trustData.fdwRevocationChecks = WTD_REVOKE_NONE;
   trustData.dwUnionChoice = WTD_CHOICE_FILE;
   trustData.pFile = &fileInfo;
-  trustData.dwStateAction = WTD_STATEACTION_VERIFY;
-  trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+
+  if (VerifySignatureInternal(trustData)) {
+    mTrustSource = TrustSource::eEmbedded;
+    return QueryObject(aFilePath);
+  }
+
+  // We didn't find anything in the binary, so now try a catalog file.
+
+  // First, we open a catalog admin context.
+  HCATADMIN rawCatAdmin;
+
+  // Windows 7 also exports the CryptCATAdminAcquireContext2 API, but it does
+  // *not* sign its binaries with SHA-256, so we use the old API in that case.
+  if (mozilla::IsWin8OrLater()) {
+    static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminAcquireContext2)>
+      pCryptCATAdminAcquireContext2(L"wintrust.dll", "CryptCATAdminAcquireContext2");
+    if (!pCryptCATAdminAcquireContext2) {
+      return false;
+    }
+
+    CERT_STRONG_SIGN_PARA policy = {sizeof(policy)};
+    policy.dwInfoChoice = CERT_STRONG_SIGN_OID_INFO_CHOICE;
+    policy.pszOID = szOID_CERT_STRONG_SIGN_OS_CURRENT;
+
+    if (!pCryptCATAdminAcquireContext2(&rawCatAdmin, nullptr,
+                                       BCRYPT_SHA256_ALGORITHM, &policy, 0)) {
+      return false;
+    }
+  } else {
+    static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminAcquireContext)>
+      pCryptCATAdminAcquireContext(L"wintrust.dll", "CryptCATAdminAcquireContext");
+
+    if (!pCryptCATAdminAcquireContext ||
+        !pCryptCATAdminAcquireContext(&rawCatAdmin, nullptr, 0)) {
+      return false;
+    }
+  }
+
+  CATAdminContextUniquePtr catAdmin(rawCatAdmin);
+
+  // Now we need to hash the file at aFilePath.
+  // Since we're hashing this file, let's open it with a sequential scan hint.
+  HANDLE rawFile = ::CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ,
+                                 nullptr, OPEN_EXISTING,
+                                 FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
+  if (rawFile == INVALID_HANDLE_VALUE) {
+    return false;
+  }
+
+  nsAutoHandle file(rawFile);
+  DWORD hashLen = 0;
+  mozilla::UniquePtr<BYTE[]> hashBuf;
+
+  static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminCalcHashFromFileHandle2)>
+    pCryptCATAdminCalcHashFromFileHandle2(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle2");
+  if (pCryptCATAdminCalcHashFromFileHandle2) {
+    if (!pCryptCATAdminCalcHashFromFileHandle2(rawCatAdmin, rawFile, &hashLen,
+                                               nullptr, 0) &&
+        ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      return false;
+    }
+
+    hashBuf = mozilla::MakeUnique<BYTE[]>(hashLen);
+
+    if (!pCryptCATAdminCalcHashFromFileHandle2(rawCatAdmin, rawFile, &hashLen,
+                                               hashBuf.get(), 0)) {
+      return false;
+    }
+  } else {
+    static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminCalcHashFromFileHandle)>
+      pCryptCATAdminCalcHashFromFileHandle(L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle");
+
+    if (!pCryptCATAdminCalcHashFromFileHandle) {
+      return false;
+    }
 
-  const HWND hwnd = (HWND) INVALID_HANDLE_VALUE;
-  GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
-  LONG result = WinVerifyTrust(hwnd, &policyGUID, &trustData);
+    if (!pCryptCATAdminCalcHashFromFileHandle(rawFile, &hashLen, nullptr, 0) &&
+        ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+      return false;
+    }
+
+    hashBuf = mozilla::MakeUnique<BYTE[]>(hashLen);
+
+    if (!pCryptCATAdminCalcHashFromFileHandle(rawFile, &hashLen, hashBuf.get(), 0)) {
+      return false;
+    }
+  }
+
+  // Now that we've hashed the file, query the catalog system to see if any
+  // catalogs reference a binary with our hash.
+
+  static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATAdminEnumCatalogFromHash)>
+    pCryptCATAdminEnumCatalogFromHash(L"wintrust.dll", "CryptCATAdminEnumCatalogFromHash");
+  if (!pCryptCATAdminEnumCatalogFromHash) {
+    return false;
+  }
+
+  HCATINFO catInfoHdl = pCryptCATAdminEnumCatalogFromHash(rawCatAdmin,
+                                                          hashBuf.get(), hashLen,
+                                                          0, nullptr);
+  if (!catInfoHdl) {
+    return false;
+  }
+
+  // We found a catalog! Now query for the path to the catalog file.
+
+  static const mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CryptCATCatalogInfoFromContext)>
+    pCryptCATCatalogInfoFromContext(L"wintrust.dll", "CryptCATCatalogInfoFromContext");
+  if (!pCryptCATCatalogInfoFromContext) {
+    return false;
+  }
 
-  trustData.dwStateAction = WTD_STATEACTION_CLOSE;
-  WinVerifyTrust(hwnd, &policyGUID, &trustData);
+  CATALOG_INFO_ catInfo = {sizeof(catInfo)};
+  if (!pCryptCATCatalogInfoFromContext(catInfoHdl, &catInfo, 0)) {
+    return false;
+  }
+
+  // WINTRUST_CATALOG_INFO::pcwszMemberTag is commonly set to the string
+  // representation of the file hash, so we build that here.
+
+  DWORD strHashBufLen = (hashLen * 2) + 1;
+  auto strHashBuf = mozilla::MakeUnique<wchar_t[]>(strHashBufLen);
+  if (!::CryptBinaryToStringW(hashBuf.get(), hashLen,
+                              CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF,
+                              strHashBuf.get(), &strHashBufLen)) {
+    return false;
+  }
 
-  return result == ERROR_SUCCESS;
+  // Now, given the path to the catalog, and the path to the member (ie, the
+  // binary whose hash we are validating), we may now validate. If the
+  // validation is successful, we then QueryObject on the *catalog file*
+  // instead of the binary.
+
+  WINTRUST_CATALOG_INFO wtCatInfo = {sizeof(wtCatInfo)};
+  wtCatInfo.pcwszCatalogFilePath = catInfo.wszCatalogFile;
+  wtCatInfo.pcwszMemberTag = strHashBuf.get();
+  wtCatInfo.pcwszMemberFilePath = aFilePath;
+  wtCatInfo.hMemberFile = rawFile;
+  if (mozilla::IsWin8OrLater()) {
+    wtCatInfo.hCatAdmin = rawCatAdmin;
+  }
+
+  trustData.dwUnionChoice = WTD_CHOICE_CATALOG;
+  trustData.pCatalog = &wtCatInfo;
+
+  if (VerifySignatureInternal(trustData)) {
+    mTrustSource = TrustSource::eCatalog;
+    return QueryObject(catInfo.wszCatalogFile);
+  }
+
+  return false;
 }
 
 mozilla::UniquePtr<wchar_t[]>
 SignedBinary::GetOrgName()
 {
   DWORD charCount = CertGetNameStringW(mCertCtx.get(),
                                        CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
                                        nullptr, nullptr, 0);
--- a/testing/mozharness/configs/releases/bouncer_firefox_esr.py
+++ b/testing/mozharness/configs/releases/bouncer_firefox_esr.py
@@ -56,30 +56,16 @@ config = {
                     "bouncer-platform": "win",
                 },
                 "win64": {
                     "path": "/firefox/releases/%(version)s/win64/:lang/Firefox%%20Setup%%20%(version)s.exe",
                     "bouncer-platform": "win64",
                 },
             },
         },
-        "sha1-installer": {
-            "product-name": "Firefox-%(version)s-sha1",
-            "check_uptake": True,
-            # XP/Vista Release users are redicted to ESR52
-            "alias": "firefox-sha1",
-            "ssl-only": True,
-            "add-locales": True,
-            "paths": {
-                "win32": {
-                    "path": "/firefox/releases/%(version)s/win32-sha1/:lang/Firefox%%20Setup%%20%(version)s.exe",
-                    "bouncer-platform": "win",
-                },
-            },
-        },
         "complete-mar": {
             "product-name": "Firefox-%(version)s-Complete",
             "check_uptake": True,
             "ssl-only": False,
             "add-locales": True,
             "paths": {
                 "linux": {
                     "path": "/firefox/releases/%(version)s/update/linux-i686/:lang/firefox-%(version)s.complete.mar",