1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "VREventObserver.h"
8 
9 #include "nsContentUtils.h"
10 #include "nsGlobalWindow.h"
11 
12 #include "mozilla/Telemetry.h"
13 
14 namespace mozilla {
15 namespace dom {
16 
17 using namespace gfx;
18 
19 /**
20  * This class is used by nsGlobalWindow to implement window.onvrdisplayactivate,
21  * window.onvrdisplaydeactivate, window.onvrdisplayconnected,
22  * window.onvrdisplaydisconnected, and window.onvrdisplaypresentchange.
23  */
VREventObserver(nsGlobalWindowInner * aGlobalWindow)24 VREventObserver::VREventObserver(nsGlobalWindowInner* aGlobalWindow)
25     : mWindow(aGlobalWindow),
26       mIs2DView(true),
27       mHasReset(false),
28       mStopActivity(false) {
29   MOZ_ASSERT(aGlobalWindow);
30 
31   UpdateSpentTimeIn2DTelemetry(false);
32   VRManagerChild* vmc = VRManagerChild::Get();
33   if (vmc) {
34     vmc->AddListener(this);
35   }
36 }
37 
~VREventObserver()38 VREventObserver::~VREventObserver() { DisconnectFromOwner(); }
39 
DisconnectFromOwner()40 void VREventObserver::DisconnectFromOwner() {
41   // In the event that nsGlobalWindow is deallocated, VREventObserver may
42   // still be AddRef'ed elsewhere.  Ensure that we don't UAF by
43   // dereferencing mWindow.
44   UpdateSpentTimeIn2DTelemetry(true);
45   mWindow = nullptr;
46 
47   // Unregister from VRManagerChild
48   if (VRManagerChild::IsCreated()) {
49     VRManagerChild* vmc = VRManagerChild::Get();
50     vmc->RemoveListener(this);
51   }
52   mStopActivity = true;
53 }
54 
UpdateSpentTimeIn2DTelemetry(bool aUpdate)55 void VREventObserver::UpdateSpentTimeIn2DTelemetry(bool aUpdate) {
56   // mHasReset for avoiding setting the telemetry continuously
57   // for the telemetry is already been set when it is at the background.
58   // then, it would be set again when the process is exit and calling
59   // VREventObserver::DisconnectFromOwner().
60   if (mWindow && mIs2DView && aUpdate && mHasReset) {
61     // The WebVR content is closed, and we will collect the telemetry info
62     // for the users who view it in 2D view only.
63     Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 0);
64     Telemetry::AccumulateTimeDelta(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_2D,
65                                    mSpendTimeIn2DView);
66     mHasReset = false;
67   } else if (!aUpdate) {
68     mSpendTimeIn2DView = TimeStamp::Now();
69     mHasReset = true;
70   }
71 }
72 
StartActivity()73 void VREventObserver::StartActivity() {
74   mStopActivity = false;
75   VRManagerChild* vmc = VRManagerChild::Get();
76   vmc->StartActivity();
77 }
78 
StopActivity()79 void VREventObserver::StopActivity() {
80   mStopActivity = true;
81   VRManagerChild* vmc = VRManagerChild::Get();
82   vmc->StopActivity();
83 }
84 
GetStopActivityStatus() const85 bool VREventObserver::GetStopActivityStatus() const { return mStopActivity; }
86 
NotifyAfterLoad()87 void VREventObserver::NotifyAfterLoad() {
88   if (VRManagerChild::IsCreated()) {
89     VRManagerChild* vmc = VRManagerChild::Get();
90     vmc->FireDOMVRDisplayConnectEventsForLoad(this);
91   }
92 }
93 
NotifyVRDisplayMounted(uint32_t aDisplayID)94 void VREventObserver::NotifyVRDisplayMounted(uint32_t aDisplayID) {
95   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
96     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
97     mWindow->DispatchVRDisplayActivate(aDisplayID,
98                                        VRDisplayEventReason::Mounted);
99   }
100 }
101 
NotifyVRDisplayNavigation(uint32_t aDisplayID)102 void VREventObserver::NotifyVRDisplayNavigation(uint32_t aDisplayID) {
103   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
104     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
105     mWindow->DispatchVRDisplayActivate(aDisplayID,
106                                        VRDisplayEventReason::Navigation);
107   }
108 }
109 
NotifyVRDisplayRequested(uint32_t aDisplayID)110 void VREventObserver::NotifyVRDisplayRequested(uint32_t aDisplayID) {
111   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
112     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
113     mWindow->DispatchVRDisplayActivate(aDisplayID,
114                                        VRDisplayEventReason::Requested);
115   }
116 }
117 
NotifyVRDisplayUnmounted(uint32_t aDisplayID)118 void VREventObserver::NotifyVRDisplayUnmounted(uint32_t aDisplayID) {
119   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
120     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
121     mWindow->DispatchVRDisplayDeactivate(aDisplayID,
122                                          VRDisplayEventReason::Unmounted);
123   }
124 }
125 
NotifyVRDisplayConnect(uint32_t aDisplayID)126 void VREventObserver::NotifyVRDisplayConnect(uint32_t aDisplayID) {
127   /**
128    * We do not call nsGlobalWindow::NotifyActiveVRDisplaysChanged here, as we
129    * can assume that a newly enumerated display is not presenting WebVR
130    * content.
131    */
132   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
133     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
134     mWindow->DispatchVRDisplayConnect(aDisplayID);
135   }
136 }
137 
NotifyVRDisplayDisconnect(uint32_t aDisplayID)138 void VREventObserver::NotifyVRDisplayDisconnect(uint32_t aDisplayID) {
139   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
140     mWindow->NotifyActiveVRDisplaysChanged();
141     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
142     mWindow->DispatchVRDisplayDisconnect(aDisplayID);
143   }
144 }
145 
NotifyVRDisplayPresentChange(uint32_t aDisplayID)146 void VREventObserver::NotifyVRDisplayPresentChange(uint32_t aDisplayID) {
147   // When switching to HMD present mode, it is no longer
148   // to be a 2D view.
149   mIs2DView = false;
150 
151   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
152     mWindow->NotifyActiveVRDisplaysChanged();
153     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
154     mWindow->DispatchVRDisplayPresentChange(aDisplayID);
155   }
156 }
157 
NotifyPresentationGenerationChanged(uint32_t aDisplayID)158 void VREventObserver::NotifyPresentationGenerationChanged(uint32_t aDisplayID) {
159   if (mWindow && mWindow->IsCurrentInnerWindow() && IsWebVR(aDisplayID)) {
160     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
161     mWindow->NotifyPresentationGenerationChanged(aDisplayID);
162   }
163 }
164 
NotifyEnumerationCompleted()165 void VREventObserver::NotifyEnumerationCompleted() {}
166 
NotifyDetectRuntimesCompleted()167 void VREventObserver::NotifyDetectRuntimesCompleted() {
168   if (mWindow && mWindow->IsCurrentInnerWindow()) {
169     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
170     mWindow->NotifyDetectXRRuntimesCompleted();
171   }
172 }
173 
IsWebVR(uint32_t aDisplayID) const174 bool VREventObserver::IsWebVR(uint32_t aDisplayID) const {
175   VRManagerChild* vmc = VRManagerChild::Get();
176   if (vmc) {
177     return vmc->GetVRAPIMode(aDisplayID) == gfx::VRAPIMode::WebVR;
178   }
179   return true;
180 }
181 
182 }  // namespace dom
183 }  // namespace mozilla
184