Bug 1293333 - Part 1: Implement window.onvrdisplayactivate and window.onvrdisplaydeactivate events draft
authorKearwood (Kip) Gilbert <kgilbert@mozilla.com>
Tue, 18 Oct 2016 18:18:10 -0400
changeset 469230 19d50e59b89cb40ccd563691d6544c136c4492f3
parent 469123 f3d187bd0733b1182dffc97b5dfe623e18f92a44
child 469231 ce13e897e1bc06ee375987711b9a3c6cc4a6a3ae
push id43661
push userbmo:kgilbert@mozilla.com
push dateWed, 01 Feb 2017 22:54:31 +0000
bugs1293333
milestone54.0a1
Bug 1293333 - Part 1: Implement window.onvrdisplayactivate and window.onvrdisplaydeactivate events MozReview-Commit-ID: 746L5KObBcg
dom/base/nsGkAtomList.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/events/EventNameList.h
dom/vr/VRDisplay.cpp
dom/vr/VRDisplay.h
dom/vr/VRDisplayEvent.cpp
dom/vr/VRDisplayEvent.h
dom/vr/VREventObserver.cpp
dom/vr/VREventObserver.h
dom/vr/moz.build
dom/webidl/VRDisplayEvent.webidl
dom/webidl/Window.webidl
dom/webidl/moz.build
gfx/vr/VRDisplayClient.cpp
gfx/vr/VRDisplayClient.h
gfx/vr/gfxVR.h
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVROpenVR.h
gfx/vr/ipc/VRManagerChild.cpp
gfx/vr/ipc/VRManagerChild.h
gfx/vr/ipc/VRMessageUtils.h
widget/EventMessageList.h
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -945,17 +945,19 @@ GK_ATOM(onunderflow, "onunderflow")
 GK_ATOM(onunload, "onunload")
 GK_ATOM(onupdatefound, "onupdatefound")
 GK_ATOM(onupdateready, "onupdateready")
 GK_ATOM(onupgradeneeded, "onupgradeneeded")
 GK_ATOM(onussdreceived, "onussdreceived")
 GK_ATOM(onversionchange, "onversionchange")
 GK_ATOM(onvoicechange, "onvoicechange")
 GK_ATOM(onvoiceschanged, "onvoiceschanged")
+GK_ATOM(onvrdisplayactivate, "onvrdisplayactivate")
 GK_ATOM(onvrdisplayconnect, "onvrdisplayconnect")
+GK_ATOM(onvrdisplaydeactivate, "onvrdisplaydeactivate")
 GK_ATOM(onvrdisplaydisconnect, "onvrdisplaydisconnect")
 GK_ATOM(onvrdisplaypresentchange, "onvrdisplaypresentchange")
 GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd")
 GK_ATOM(onwebkitAnimationIteration, "onwebkitAnimationIteration")
 GK_ATOM(onwebkitAnimationStart, "onwebkitAnimationStart")
 GK_ATOM(onwebkitTransitionEnd, "onwebkitTransitionEnd")
 GK_ATOM(onwebkitanimationend, "onwebkitanimationend")
 GK_ATOM(onwebkitanimationiteration, "onwebkitanimationiteration")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -196,16 +196,18 @@
 #include "mozilla/dom/IDBFactory.h"
 #include "mozilla/dom/MessageChannel.h"
 #include "mozilla/dom/Promise.h"
 
 #include "mozilla/dom/Gamepad.h"
 #include "mozilla/dom/GamepadManager.h"
 
 #include "mozilla/dom/VRDisplay.h"
+#include "mozilla/dom/VRDisplayEvent.h"
+#include "mozilla/dom/VRDisplayEventBinding.h"
 #include "mozilla/dom/VREventObserver.h"
 
 #include "nsRefreshDriver.h"
 #include "Layers.h"
 
 #include "mozilla/AddonPathService.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Services.h"
@@ -12933,17 +12935,19 @@ nsGlobalWindow::SetHasGamepadEventListen
     EnableGamepadUpdates();
   }
 }
 
 
 void
 nsGlobalWindow::EventListenerAdded(nsIAtom* aType)
 {
-  if (aType == nsGkAtoms::onvrdisplayconnect ||
+  if (aType == nsGkAtoms::onvrdisplayactivate ||
+      aType == nsGkAtoms::onvrdisplayconnect ||
+      aType == nsGkAtoms::onvrdisplaydeactivate ||
       aType == nsGkAtoms::onvrdisplaydisconnect ||
       aType == nsGkAtoms::onvrdisplaypresentchange) {
     NotifyVREventListenerAdded();
   }
 }
 
 void
 nsGlobalWindow::NotifyVREventListenerAdded()
@@ -13107,16 +13111,72 @@ nsGlobalWindow::NotifyActiveVRDisplaysCh
 {
   MOZ_ASSERT(IsInnerWindow());
 
   if (mNavigator) {
     mNavigator->NotifyActiveVRDisplaysChanged();
   }
 }
 
+void
+nsGlobalWindow::DispatchVRDisplayActivate(uint32_t aDisplayID,
+                                          mozilla::dom::VRDisplayEventReason aReason)
+{
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID
+        && !display->IsAnyPresenting()) {
+      // We only want to trigger this event if nobody is presenting to the
+      // display already.
+
+      VRDisplayEventInit init;
+      init.mBubbles = true;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      init.mReason.Construct(aReason);
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplayactivate"),
+                                    init);
+      // vrdisplayactivate is a trusted event, allowing VRDisplay.requestPresent
+      // to be used in response to link traversal, user request (chrome UX), and
+      // HMD mounting detection sensors.
+      event->SetTrusted(true);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      break;
+    }
+  }
+}
+
+void
+nsGlobalWindow::DispatchVRDisplayDeactivate(uint32_t aDisplayID,
+                                            mozilla::dom::VRDisplayEventReason aReason)
+{
+  for (auto display : mVRDisplays) {
+    if (display->DisplayId() == aDisplayID && display->IsPresenting()) {
+      // We only want to trigger this event to content that is presenting to
+      // the display already.
+
+      VRDisplayEventInit init;
+      init.mBubbles = true;
+      init.mCancelable = false;
+      init.mDisplay = display;
+      init.mReason.Construct(aReason);
+
+      RefPtr<VRDisplayEvent> event =
+        VRDisplayEvent::Constructor(this,
+                                    NS_LITERAL_STRING("vrdisplaydeactivate"),
+                                    init);
+      bool defaultActionEnabled;
+      Unused << DispatchEvent(event, &defaultActionEnabled);
+      break;
+    }
+  }
+}
 
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                   nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -129,16 +129,17 @@ class PostMessageEvent;
 struct RequestInit;
 class RequestOrUSVString;
 class Selection;
 class SpeechSynthesis;
 class TabGroup;
 class Timeout;
 class U2F;
 class VRDisplay;
+enum class VRDisplayEventReason : uint8_t;
 class VREventObserver;
 class WakeLock;
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class WindowOrientationObserver;
 #endif
 class Worklet;
 namespace cache {
 class CacheStorage;
@@ -750,16 +751,21 @@ public:
 
   // Update the VR displays for this window
   bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
 
   // Inner windows only.
   // Called to inform that the set of active VR displays has changed.
   void NotifyActiveVRDisplaysChanged();
 
+  void DispatchVRDisplayActivate(uint32_t aDisplayID,
+                                 mozilla::dom::VRDisplayEventReason aReason);
+  void DispatchVRDisplayDeactivate(uint32_t aDisplayID,
+                                   mozilla::dom::VRDisplayEventReason aReason);
+
 #define EVENT(name_, id_, type_, struct_)                                     \
   mozilla::dom::EventHandlerNonNull* GetOn##name_()                           \
   {                                                                           \
     mozilla::EventListenerManager* elm = GetExistingListenerManager();        \
     return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString())    \
                : nullptr;                                                     \
   }                                                                           \
   void SetOn##name_(mozilla::dom::EventHandlerNonNull* handler)               \
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -602,16 +602,24 @@ WINDOW_ONLY_EVENT(deviceproximity,
 WINDOW_ONLY_EVENT(userproximity,
                   eUserProximity,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(devicelight,
                   eDeviceLight,
                   EventNameType_None,
                   eBasicEventClass)
+WINDOW_ONLY_EVENT(vrdisplayactivate,
+                  eVRDisplayActivate,
+                  EventNameType_None,
+                  eBasicEventClass)
+WINDOW_ONLY_EVENT(vrdisplaydeactivate,
+                  eVRDisplayDeactivate,
+                  EventNameType_None,
+                  eBasicEventClass)
 WINDOW_ONLY_EVENT(vrdisplayconnect,
                   eVRDisplayConnect,
                   EventNameType_None,
                   eBasicEventClass)
 WINDOW_ONLY_EVENT(vrdisplaydisconnect,
                   eVRDisplayDisconnect,
                   EventNameType_None,
                   eBasicEventClass)
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -459,19 +459,22 @@ VRDisplay::RequestPresent(const nsTArray
   }
 
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE(obs, nullptr);
 
-  if (mClient->GetIsPresenting()) {
+  if (!IsPresenting() && IsAnyPresenting()) {
     // Only one presentation allowed per VRDisplay
     // on a first-come-first-serve basis.
+    // If this Javascript context is presenting, then we can replace our
+    // presentation with a new one containing new layers but we should never
+    // replace the presentation of another context.
     promise->MaybeRejectWithUndefined();
   } else {
     mPresentation = mClient->BeginPresentation(aLayers);
     mFrameInfo.Clear();
 
     nsresult rv = obs->AddObserver(this, "inner-window-destroyed", false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mPresentation = nullptr;
@@ -581,16 +584,24 @@ bool
 VRDisplay::IsPresenting() const
 {
   // IsPresenting returns true only if this Javascript context is presenting
   // and will return false if another context is presenting.
   return mPresentation != nullptr;
 }
 
 bool
+VRDisplay::IsAnyPresenting() const
+{
+  // IsAnyPresenting returns true if any Javascript context is presenting
+  // even if this context is not presenting.
+  return IsPresenting() || mClient->GetIsPresenting();
+}
+
+bool
 VRDisplay::IsConnected() const
 {
   return mClient->GetIsConnected();
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper, mCapabilities, mStageParameters)
 
 NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper)
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -269,16 +269,17 @@ class VRDisplay final : public DOMEventT
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIOBSERVER
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(VRDisplay, DOMEventTargetHelper)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   bool IsPresenting() const;
+  bool IsAnyPresenting() const;
   bool IsConnected() const;
 
   VRDisplayCapabilities* Capabilities();
   VRStageParameters* GetStageParameters();
 
   uint32_t DisplayId() const { return mDisplayId; }
   void GetDisplayName(nsAString& aDisplayName) const { aDisplayName = mDisplayName; }
 
new file mode 100644
--- /dev/null
+++ b/dom/vr/VRDisplayEvent.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VRDisplayEvent.h"
+#include "js/GCAPI.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/PrimitiveConversions.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(VRDisplayEvent)
+
+NS_IMPL_ADDREF_INHERITED(VRDisplayEvent, Event)
+NS_IMPL_RELEASE_INHERITED(VRDisplayEvent, Event)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(VRDisplayEvent, Event)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(VRDisplayEvent, Event)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(VRDisplayEvent, Event)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(VRDisplayEvent)
+NS_INTERFACE_MAP_END_INHERITING(Event)
+
+VRDisplayEvent::VRDisplayEvent(mozilla::dom::EventTarget* aOwner)
+  : Event(aOwner, nullptr, nullptr)
+{
+}
+
+VRDisplayEvent::~VRDisplayEvent()
+{
+}
+
+VRDisplay*
+VRDisplayEvent::Display()
+{
+  return mDisplay;
+}
+
+JSObject*
+VRDisplayEvent::WrapObjectInternal(JSContext* aCx,
+                                   JS::Handle<JSObject*> aGivenProto)
+{
+  return VRDisplayEventBinding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<VRDisplayEvent>
+VRDisplayEvent::Constructor(mozilla::dom::EventTarget* aOwner,
+                            const nsAString& aType,
+                            const VRDisplayEventInit& aEventInitDict)
+{
+  RefPtr<VRDisplayEvent> e = new VRDisplayEvent(aOwner);
+  bool trusted = e->Init(aOwner);
+  e->InitEvent(aType, aEventInitDict.mBubbles, aEventInitDict.mCancelable);
+  if (aEventInitDict.mReason.WasPassed()) {
+    e->mReason = Some(aEventInitDict.mReason.Value());
+  }
+  e->SetTrusted(trusted);
+  e->SetComposed(aEventInitDict.mComposed);
+  return e.forget();
+}
+
+already_AddRefed<VRDisplayEvent>
+VRDisplayEvent::Constructor(const GlobalObject& aGlobal, const nsAString& aType,
+                            const VRDisplayEventInit& aEventInitDict,
+                            ErrorResult& aRv)
+{
+  nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
+  return Constructor(owner, aType, aEventInitDict);
+}
+
+Nullable<VRDisplayEventReason>
+VRDisplayEvent::GetReason() const
+{
+  if (mReason.isSome()) {
+    return mReason.value();
+  } else {
+    return nullptr;
+  }
+  
+}
+
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/vr/VRDisplayEvent.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_VRDisplayEvent_h_
+#define mozilla_dom_VRDisplayEvent_h_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/VRDisplayEventBinding.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Event.h"
+
+#include "gfxVR.h"
+
+struct JSContext;
+
+namespace mozilla {
+namespace gfx {
+class VRDisplay;
+} // namespace gfx
+
+namespace dom {
+
+class VRDisplayEvent final : public Event
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(VRDisplayEvent, Event)
+
+  VRDisplay* Display();
+  Nullable<VRDisplayEventReason> GetReason() const;
+
+protected:
+  virtual ~VRDisplayEvent();
+  explicit VRDisplayEvent(mozilla::dom::EventTarget* aOwner);
+  VRDisplayEvent(EventTarget* aOwner,
+                 nsPresContext* aPresContext,
+                 InternalClipboardEvent* aEvent);
+
+  Maybe<VRDisplayEventReason> mReason;
+  RefPtr<VRDisplay> mDisplay;
+
+public:
+
+  virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  static already_AddRefed<VRDisplayEvent> Constructor(mozilla::dom::EventTarget* aOwner, const nsAString& aType, const VRDisplayEventInit& aEventInitDict);
+
+  static already_AddRefed<VRDisplayEvent> Constructor(const GlobalObject& aGlobal, const nsAString& aType, const VRDisplayEventInit& aEventInitDict, ErrorResult& aRv);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/vr/VREventObserver.cpp
+++ b/dom/vr/VREventObserver.cpp
@@ -11,17 +11,18 @@
 #include "VRManagerChild.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace gfx;
 
 /**
- * This class is used by nsGlobalWindow to implement window.onvrdisplayconnected,
+ * This class is used by nsGlobalWindow to implement window.onvrdisplayactivate,
+ * window.onvrdisplaydeactivate, window.onvrdisplayconnected,
  * window.onvrdisplaydisconnected, and window.onvrdisplaypresentchange.
  */
 VREventObserver::VREventObserver(nsGlobalWindow* aGlobalWindow)
   : mWindow(aGlobalWindow)
 {
   MOZ_ASSERT(aGlobalWindow && aGlobalWindow->IsInnerWindow());
 
   VRManagerChild* vmc = VRManagerChild::Get();
@@ -34,16 +35,56 @@ VREventObserver::~VREventObserver()
 {
   VRManagerChild* vmc = VRManagerChild::Get();
   if (vmc) {
     vmc->RemoveListener(this);
   }
 }
 
 void
+VREventObserver::NotifyVRDisplayMounted(uint32_t aDisplayID)
+{
+  if (mWindow->AsInner()->IsCurrentInnerWindow()) {
+    MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+    mWindow->DispatchVRDisplayActivate(aDisplayID,
+                                       VRDisplayEventReason::Mounted);
+  }
+}
+
+void
+VREventObserver::NotifyVRDisplayNavigation(uint32_t aDisplayID)
+{
+  if (mWindow->AsInner()->IsCurrentInnerWindow()) {
+    MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+    mWindow->DispatchVRDisplayActivate(aDisplayID,
+                                       VRDisplayEventReason::Navigation);
+  }
+}
+
+void
+VREventObserver::NotifyVRDisplayRequested(uint32_t aDisplayID)
+{
+  if (mWindow->AsInner()->IsCurrentInnerWindow()) {
+    MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+    mWindow->DispatchVRDisplayActivate(aDisplayID,
+                                       VRDisplayEventReason::Requested);
+  }
+}
+
+void
+VREventObserver::NotifyVRDisplayUnmounted(uint32_t aDisplayID)
+{
+  if (mWindow->AsInner()->IsCurrentInnerWindow()) {
+    MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+    mWindow->DispatchVRDisplayDeactivate(aDisplayID,
+                                         VRDisplayEventReason::Unmounted);
+  }
+}
+
+void
 VREventObserver::NotifyVRDisplayConnect()
 {
   /**
    * We do not call nsGlobalWindow::NotifyActiveVRDisplaysChanged here, as we
    * can assume that a newly enumerated display is not presenting WebVR
    * content.
    */
   if (mWindow->AsInner()->IsCurrentInnerWindow()) {
--- a/dom/vr/VREventObserver.h
+++ b/dom/vr/VREventObserver.h
@@ -2,27 +2,33 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_VREventObserver_h
 #define mozilla_dom_VREventObserver_h
 
+#include "mozilla/dom/VRDisplayEventBinding.h"
+
 class nsGlobalWindow;
 
 namespace mozilla {
 namespace dom {
 
 class VREventObserver final
 {
 public:
   ~VREventObserver();
   explicit VREventObserver(nsGlobalWindow* aGlobalWindow);
 
+  void NotifyVRDisplayMounted(uint32_t aDisplayID);
+  void NotifyVRDisplayUnmounted(uint32_t aDisplayID);
+  void NotifyVRDisplayNavigation(uint32_t aDisplayID);
+  void NotifyVRDisplayRequested(uint32_t aDisplayID);
   void NotifyVRDisplayConnect();
   void NotifyVRDisplayDisconnect();
   void NotifyVRDisplayPresentChange();
 
 private:
   // Weak pointer, instance is owned by mWindow.
   nsGlobalWindow* MOZ_NON_OWNING_REF mWindow;
 };
--- a/dom/vr/moz.build
+++ b/dom/vr/moz.build
@@ -1,21 +1,23 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'VRDisplay.h',
+    'VRDisplayEvent.h',
     'VREventObserver.h',
     ]
 
 UNIFIED_SOURCES = [
     'VRDisplay.cpp',
+    'VRDisplayEvent.cpp',
     'VREventObserver.cpp',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base'
new file mode 100644
--- /dev/null
+++ b/dom/webidl/VRDisplayEvent.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+enum VRDisplayEventReason {
+  "mounted",
+  "navigation",
+  "requested",
+  "unmounted",
+};
+
+dictionary VRDisplayEventInit : EventInit {
+  required VRDisplay display;
+  VRDisplayEventReason reason;
+};
+
+[Pref="dom.vr.enabled",
+ Constructor(DOMString type, VRDisplayEventInit eventInitDict)]
+interface VRDisplayEvent : Event {
+  readonly attribute VRDisplay display;
+  readonly attribute VRDisplayEventReason? reason;
+};
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -467,16 +467,20 @@ interface ChromeWindow {
 };
 
 partial interface Window {
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplayconnect;
   [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplaydisconnect;
   [Pref="dom.vr.enabled"]
+  attribute EventHandler onvrdisplayactivate;
+  [Pref="dom.vr.enabled"]
+  attribute EventHandler onvrdisplaydeactivate;
+  [Pref="dom.vr.enabled"]
   attribute EventHandler onvrdisplaypresentchange;
 };
 
 // https://webaudio.github.io/web-audio-api/#widl-Window-audioWorklet
 partial interface Window {
   [Pref="dom.audioWorklet.enabled", Throws, SameObject]
   readonly attribute Worklet audioWorklet;
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -568,16 +568,17 @@ WEBIDL_FILES = [
     'URL.webidl',
     'URLSearchParams.webidl',
     'ValidityState.webidl',
     'VideoPlaybackQuality.webidl',
     'VideoStreamTrack.webidl',
     'VideoTrack.webidl',
     'VideoTrackList.webidl',
     'VRDisplay.webidl',
+    'VRDisplayEvent.webidl',
     'VTTCue.webidl',
     'VTTRegion.webidl',
     'WaveShaperNode.webidl',
     'WebAuthentication.webidl',
     'WebComponents.webidl',
     'WebGL2RenderingContext.webidl',
     'WebGLRenderingContext.webidl',
     'WebKitCSSMatrix.webidl',
--- a/gfx/vr/VRDisplayClient.cpp
+++ b/gfx/vr/VRDisplayClient.cpp
@@ -24,16 +24,17 @@
 #include "VRManagerChild.h"
 #include "VRLayerChild.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
   : mDisplayInfo(aDisplayInfo)
+  , bLastEventWasMounted(false)
   , bLastEventWasPresenting(false)
   , mPresentationCount(0)
 {
   MOZ_COUNT_CTOR(VRDisplayClient);
 }
 
 VRDisplayClient::~VRDisplayClient() {
   MOZ_COUNT_DTOR(VRDisplayClient);
@@ -109,16 +110,28 @@ VRDisplayClient::NotifyVsync()
     mLastVSyncTime = TimeStamp::Now();
   }
 
   // Check if we need to trigger onVRDisplayPresentChange event
   if (bLastEventWasPresenting != isPresenting) {
     bLastEventWasPresenting = isPresenting;
     vm->FireDOMVRDisplayPresentChangeEvent();
   }
+
+  // Check if we need to trigger onvrdisplayactivate event
+  if (!bLastEventWasMounted && mDisplayInfo.mIsMounted) {
+    bLastEventWasMounted = true;
+    vm->FireDOMVRDisplayMountedEvent(mDisplayInfo.mDisplayID);
+  }
+
+  // Check if we need to trigger onvrdisplaydeactivate event
+  if (bLastEventWasMounted && !mDisplayInfo.mIsMounted) {
+    bLastEventWasMounted = false;
+    vm->FireDOMVRDisplayUnmountedEvent(mDisplayInfo.mDisplayID);
+  }
 }
 
 void
 VRDisplayClient::NotifyVRVsync()
 {
   VRManagerChild *vm = VRManagerChild::Get();
   vm->RunFrameRequestCallbacks();
   mLastVSyncTime = TimeStamp::Now();
--- a/gfx/vr/VRDisplayClient.h
+++ b/gfx/vr/VRDisplayClient.h
@@ -44,16 +44,17 @@ public:
 
   void NotifyDisconnected();
 
 protected:
   virtual ~VRDisplayClient();
 
   VRDisplayInfo mDisplayInfo;
 
+  bool bLastEventWasMounted;
   bool bLastEventWasPresenting;
 
   TimeStamp mLastVSyncTime;
   int mPresentationCount;
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -74,19 +74,24 @@ enum class VRDisplayCapabilityFlags : ui
    */
   Cap_LinearAcceleration = 1 << 6,
   /**
    * Cap_StageParameters is set if the VRDisplay is capable of room scale VR
    * and can report the StageParameters to describe the space.
    */
   Cap_StageParameters = 1 << 7,
   /**
+   * Cap_MountDetection is set if the VRDisplay is capable of sensing when the
+   * user is wearing the device.
+   */
+  Cap_MountDetection = 1 << 8,
+  /**
    * Cap_All used for validity checking during IPC serialization
    */
-  Cap_All = (1 << 8) - 1
+  Cap_All = (1 << 9) - 1
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
 
 struct VRFieldOfView {
   VRFieldOfView() {}
   VRFieldOfView(double up, double right, double down, double left)
     : upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
@@ -132,16 +137,17 @@ struct VRDisplayInfo
   uint32_t GetDisplayID() const { return mDisplayID; }
   const nsCString& GetDisplayName() const { return mDisplayName; }
   VRDisplayCapabilityFlags GetCapabilities() const { return mCapabilityFlags; }
 
   const IntSize& SuggestedEyeResolution() const { return mEyeResolution; }
   const Point3D& GetEyeTranslation(uint32_t whichEye) const { return mEyeTranslation[whichEye]; }
   const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const { return mEyeFOV[whichEye]; }
   bool GetIsConnected() const { return mIsConnected; }
+  bool GetIsMounted() const { return mIsMounted; }
   bool GetIsPresenting() const { return mIsPresenting; }
   const Size& GetStageSize() const { return mStageSize; }
   const Matrix4x4& GetSittingToStandingTransform() const { return mSittingToStandingTransform; }
 
   enum Eye {
     Eye_Left,
     Eye_Right,
     NumEyes
@@ -150,27 +156,29 @@ struct VRDisplayInfo
   uint32_t mDisplayID;
   VRDeviceType mType;
   nsCString mDisplayName;
   VRDisplayCapabilityFlags mCapabilityFlags;
   VRFieldOfView mEyeFOV[VRDisplayInfo::NumEyes];
   Point3D mEyeTranslation[VRDisplayInfo::NumEyes];
   IntSize mEyeResolution;
   bool mIsConnected;
+  bool mIsMounted;
   bool mIsPresenting;
   Size mStageSize;
   Matrix4x4 mSittingToStandingTransform;
 
   bool operator==(const VRDisplayInfo& other) const {
     return mType == other.mType &&
            mDisplayID == other.mDisplayID &&
            mDisplayName == other.mDisplayName &&
            mCapabilityFlags == other.mCapabilityFlags &&
            mEyeResolution == other.mEyeResolution &&
            mIsConnected == other.mIsConnected &&
+           mIsMounted == other.mIsMounted &&
            mIsPresenting == other.mIsPresenting &&
            mEyeFOV[0] == other.mEyeFOV[0] &&
            mEyeFOV[1] == other.mEyeFOV[1] &&
            mEyeTranslation[0] == other.mEyeTranslation[0] &&
            mEyeTranslation[1] == other.mEyeTranslation[1] &&
            mStageSize == other.mStageSize &&
            mSittingToStandingTransform == other.mSittingToStandingTransform;
   }
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -330,29 +330,31 @@ VRDisplayOculus::VRDisplayOculus(ovrSess
   , mVertexBuffer(nullptr)
   , mInputLayout(nullptr)
   , mIsPresenting(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayHost);
 
   mDisplayInfo.mDisplayName.AssignLiteral("Oculus VR HMD");
   mDisplayInfo.mIsConnected = true;
+  mDisplayInfo.mIsMounted = false;
 
   mDesc = ovr_GetHmdDesc(aSession);
 
   mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
   if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
   }
   if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
     mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
   }
   mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
+  mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
   mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
 
   mFOVPort[VRDisplayInfo::Eye_Left] = mDesc.DefaultEyeFov[ovrEye_Left];
   mFOVPort[VRDisplayInfo::Eye_Right] = mDesc.DefaultEyeFov[ovrEye_Right];
 
   mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Left] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Left]);
   mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Right] = FromFovPort(mFOVPort[VRDisplayInfo::Eye_Right]);
 
@@ -464,16 +466,17 @@ VRDisplayOculus::GetSensorState(double t
 
     result.flags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
 
     result.linearAcceleration[0] = pose.LinearAcceleration.x;
     result.linearAcceleration[1] = pose.LinearAcceleration.y;
     result.linearAcceleration[2] = pose.LinearAcceleration.z;
   }
   result.flags |= VRDisplayCapabilityFlags::Cap_External;
+  result.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
   result.flags |= VRDisplayCapabilityFlags::Cap_Present;
 
   return result;
 }
 
 void
 VRDisplayOculus::StartPresentation()
 {
@@ -932,9 +935,10 @@ VRDisplayOculus::SubmitFrame(TextureSour
 }
 
 void
 VRDisplayOculus::NotifyVSync()
 {
   ovrSessionStatus sessionStatus;
   ovrResult ovr = ovr_GetSessionStatus(mSession, &sessionStatus);
   mDisplayInfo.mIsConnected = (ovr == ovrSuccess && sessionStatus.HmdPresent);
+  mDisplayInfo.mIsMounted = (ovr == ovrSuccess && sessionStatus.HmdMounted);
 }
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -132,23 +132,30 @@ VRDisplayOpenVR::VRDisplayOpenVR(::vr::I
   , mVRChaperone(aVRChaperone)
   , mVRCompositor(aVRCompositor)
   , mIsPresenting(false)
 {
   MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
 
   mDisplayInfo.mDisplayName.AssignLiteral("OpenVR HMD");
   mDisplayInfo.mIsConnected = true;
+  mDisplayInfo.mIsMounted = false;
   mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
                                   VRDisplayCapabilityFlags::Cap_Orientation |
                                   VRDisplayCapabilityFlags::Cap_Position |
                                   VRDisplayCapabilityFlags::Cap_External |
                                   VRDisplayCapabilityFlags::Cap_Present |
                                   VRDisplayCapabilityFlags::Cap_StageParameters;
 
+  ::vr::ETrackedPropertyError err;
+  bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
+  if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
+    mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
+  }
+
   mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
 
   uint32_t w, h;
   mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
   mDisplayInfo.mEyeResolution.width = w;
   mDisplayInfo.mEyeResolution.height = h;
 
   // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
@@ -253,25 +260,41 @@ VRDisplayOpenVR::GetSensorState()
 }
 
 VRHMDSensorState
 VRDisplayOpenVR::GetImmediateSensorState()
 {
   return GetSensorState(0.0f);
 }
 
+void
+VRDisplayOpenVR::PollEvents()
+{
+  ::vr::VREvent_t event;
+  while (mVRSystem->PollNextEvent(&event, sizeof(event))) {
+    if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
+      switch (event.eventType) {
+      case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
+        mDisplayInfo.mIsMounted = true;
+        break;
+      case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
+        mDisplayInfo.mIsMounted = false;
+        break;
+      default:
+        // ignore
+        break;
+      }
+    }
+  }
+}
+
 VRHMDSensorState
 VRDisplayOpenVR::GetSensorState(double timeOffset)
 {
-  {
-    ::vr::VREvent_t event;
-    while (mVRSystem->PollNextEvent(&event, sizeof(event))) {
-      // ignore
-    }
-  }
+  PollEvents();
 
   ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
   // Note: We *must* call WaitGetPoses in order for any rendering to happen at all
   mVRCompositor->WaitGetPoses(poses, ::vr::k_unMaxTrackedDeviceCount, nullptr, 0);
 
   VRHMDSensorState result;
   result.Clear();
   result.timestamp = PR_Now();
@@ -387,16 +410,19 @@ VRDisplayOpenVR::SubmitFrame(TextureSour
 
 #endif
 
 void
 VRDisplayOpenVR::NotifyVSync()
 {
   // We update mIsConneced once per frame.
   mDisplayInfo.mIsConnected = vr_IsHmdPresent();
+
+  // Make sure we respond to OpenVR events even when not presenting
+  PollEvents();
 }
 
 VRSystemManagerOpenVR::VRSystemManagerOpenVR()
   : mVRSystem(nullptr), mOpenVRInstalled(false)
 {
 }
 
 /*static*/ already_AddRefed<VRSystemManagerOpenVR>
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -61,16 +61,17 @@ protected:
   // not owned by us; global from OpenVR
   ::vr::IVRSystem *mVRSystem;
   ::vr::IVRChaperone *mVRChaperone;
   ::vr::IVRCompositor *mVRCompositor;
 
   bool mIsPresenting;
 
   void UpdateStageParameters();
+  void PollEvents();
 };
 
 class VRControllerOpenVR : public VRControllerHost
 {
 public:
   explicit VRControllerOpenVR();
   void SetTrackedIndex(uint32_t aTrackedIndex);
   uint32_t GetTrackedIndex();
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -501,16 +501,32 @@ VRManagerChild::RunFrameRequestCallbacks
   callbacks.AppendElements(mFrameRequestCallbacks);
   mFrameRequestCallbacks.Clear();
   for (auto& callback : callbacks) {
     callback.mCallback->Call(timeStamp);
   }
 }
 
 void
+VRManagerChild::FireDOMVRDisplayMountedEvent(uint32_t aDisplayID)
+{
+  nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(this,
+    &VRManagerChild::FireDOMVRDisplayMountedEventInternal,
+    aDisplayID));
+}
+
+void
+VRManagerChild::FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID)
+{
+  nsContentUtils::AddScriptRunner(NewRunnableMethod<uint32_t>(this,
+    &VRManagerChild::FireDOMVRDisplayUnmountedEventInternal,
+    aDisplayID));
+}
+
+void
 VRManagerChild::FireDOMVRDisplayConnectEvent()
 {
   nsContentUtils::AddScriptRunner(NewRunnableMethod(this,
     &VRManagerChild::FireDOMVRDisplayConnectEventInternal));
 }
 
 void
 VRManagerChild::FireDOMVRDisplayDisconnectEvent()
@@ -522,16 +538,32 @@ VRManagerChild::FireDOMVRDisplayDisconne
 void
 VRManagerChild::FireDOMVRDisplayPresentChangeEvent()
 {
   nsContentUtils::AddScriptRunner(NewRunnableMethod(this,
     &VRManagerChild::FireDOMVRDisplayPresentChangeEventInternal));
 }
 
 void
+VRManagerChild::FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID)
+{
+  for (auto& listener : mListeners) {
+    listener->NotifyVRDisplayMounted(aDisplayID);
+  }
+}
+
+void
+VRManagerChild::FireDOMVRDisplayUnmountedEventInternal(uint32_t aDisplayID)
+{
+  for (auto& listener : mListeners) {
+    listener->NotifyVRDisplayUnmounted(aDisplayID);
+  }
+}
+
+void
 VRManagerChild::FireDOMVRDisplayConnectEventInternal()
 {
   for (auto& listener : mListeners) {
     listener->NotifyVRDisplayConnect();
   }
 }
 
 void
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -75,16 +75,18 @@ public:
   virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
 
   nsresult ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback& aCallback,
     int32_t *aHandle);
   void CancelFrameRequestCallback(int32_t aHandle);
   void RunFrameRequestCallbacks();
 
   void UpdateDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayUpdates);
+  void FireDOMVRDisplayMountedEvent(uint32_t aDisplayID);
+  void FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID);
   void FireDOMVRDisplayConnectEvent();
   void FireDOMVRDisplayDisconnectEvent();
   void FireDOMVRDisplayPresentChangeEvent();
 
   virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
 
 protected:
   explicit VRManagerChild();
@@ -133,16 +135,18 @@ protected:
   {
     return OtherPid() == base::GetCurrentProcId();
   }
 
   friend class layers::CompositorBridgeChild;
 
 private:
 
+  void FireDOMVRDisplayMountedEventInternal(uint32_t aDisplayID);
+  void FireDOMVRDisplayUnmountedEventInternal(uint32_t aDisplayID);
   void FireDOMVRDisplayConnectEventInternal();
   void FireDOMVRDisplayDisconnectEventInternal();
   void FireDOMVRDisplayPresentChangeEventInternal();
   /**
   * Notify id of Texture When host side end its use. Transaction id is used to
   * make sure if there is no newer usage.
   */
   void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
--- a/gfx/vr/ipc/VRMessageUtils.h
+++ b/gfx/vr/ipc/VRMessageUtils.h
@@ -34,16 +34,17 @@ struct ParamTraits<mozilla::gfx::VRDispl
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mType);
     WriteParam(aMsg, aParam.mDisplayID);
     WriteParam(aMsg, aParam.mDisplayName);
     WriteParam(aMsg, aParam.mCapabilityFlags);
     WriteParam(aMsg, aParam.mEyeResolution);
     WriteParam(aMsg, aParam.mIsConnected);
+    WriteParam(aMsg, aParam.mIsMounted);
     WriteParam(aMsg, aParam.mIsPresenting);
     WriteParam(aMsg, aParam.mStageSize);
     WriteParam(aMsg, aParam.mSittingToStandingTransform);
     for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
       WriteParam(aMsg, aParam.mEyeFOV[i]);
       WriteParam(aMsg, aParam.mEyeTranslation[i]);
     }
   }
@@ -51,16 +52,17 @@ struct ParamTraits<mozilla::gfx::VRDispl
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->mType)) ||
         !ReadParam(aMsg, aIter, &(aResult->mDisplayID)) ||
         !ReadParam(aMsg, aIter, &(aResult->mDisplayName)) ||
         !ReadParam(aMsg, aIter, &(aResult->mCapabilityFlags)) ||
         !ReadParam(aMsg, aIter, &(aResult->mEyeResolution)) ||
         !ReadParam(aMsg, aIter, &(aResult->mIsConnected)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mIsMounted)) ||
         !ReadParam(aMsg, aIter, &(aResult->mIsPresenting)) ||
         !ReadParam(aMsg, aIter, &(aResult->mStageSize)) ||
         !ReadParam(aMsg, aIter, &(aResult->mSittingToStandingTransform))) {
       return false;
     }
     for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
       if (!ReadParam(aMsg, aIter, &(aResult->mEyeFOV[i])) ||
           !ReadParam(aMsg, aIter, &(aResult->mEyeTranslation[i]))) {
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -373,16 +373,18 @@ NS_EVENT_MESSAGE(eDeviceMotion)
 NS_EVENT_MESSAGE(eDeviceProximity)
 NS_EVENT_MESSAGE(eUserProximity)
 NS_EVENT_MESSAGE(eDeviceLight)
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 NS_EVENT_MESSAGE(eOrientationChange)
 #endif
 
 // WebVR events
+NS_EVENT_MESSAGE(eVRDisplayActivate)
+NS_EVENT_MESSAGE(eVRDisplayDeactivate)
 NS_EVENT_MESSAGE(eVRDisplayConnect)
 NS_EVENT_MESSAGE(eVRDisplayDisconnect)
 NS_EVENT_MESSAGE(eVRDisplayPresentChange)
 
 NS_EVENT_MESSAGE(eShow)
 
 // Fullscreen DOM API
 NS_EVENT_MESSAGE(eFullscreenChange)