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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsWrapperCache.h"
9 #include "mozilla/dom/Element.h"
10 #include "mozilla/dom/ElementBinding.h"
11 #include "mozilla/dom/Promise.h"
12 #include "mozilla/dom/UserActivation.h"
13 #include "mozilla/dom/VRDisplay.h"
14 #include "mozilla/dom/VRDisplayBinding.h"
15 #include "mozilla/HoldDropJSObjects.h"
16 #include "mozilla/Base64.h"
17 #include "mozilla/ProfilerMarkers.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/StaticPrefs_dom.h"
20 #include "mozilla/gfx/DataSurfaceHelpers.h"
21 #include "Navigator.h"
24 #include "VRDisplayClient.h"
25 #include "VRManagerChild.h"
26 #include "VRDisplayPresentation.h"
27 #include "nsIObserverService.h"
29 #include "nsISupportsPrimitives.h"
31 using namespace mozilla::gfx
;
36 VRFieldOfView::VRFieldOfView(nsISupports
* aParent
, double aUpDegrees
,
37 double aRightDegrees
, double aDownDegrees
,
40 mUpDegrees(aUpDegrees
),
41 mRightDegrees(aRightDegrees
),
42 mDownDegrees(aDownDegrees
),
43 mLeftDegrees(aLeftDegrees
) {}
45 VRFieldOfView::VRFieldOfView(nsISupports
* aParent
,
46 const gfx::VRFieldOfView
& aSrc
)
48 mUpDegrees(aSrc
.upDegrees
),
49 mRightDegrees(aSrc
.rightDegrees
),
50 mDownDegrees(aSrc
.downDegrees
),
51 mLeftDegrees(aSrc
.leftDegrees
) {}
53 bool VRDisplayCapabilities::HasPosition() const {
54 return bool(mFlags
& gfx::VRDisplayCapabilityFlags::Cap_Position
) ||
55 bool(mFlags
& gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated
);
58 bool VRDisplayCapabilities::HasOrientation() const {
59 return bool(mFlags
& gfx::VRDisplayCapabilityFlags::Cap_Orientation
);
62 bool VRDisplayCapabilities::HasExternalDisplay() const {
63 return bool(mFlags
& gfx::VRDisplayCapabilityFlags::Cap_External
);
66 bool VRDisplayCapabilities::CanPresent() const {
67 return bool(mFlags
& gfx::VRDisplayCapabilityFlags::Cap_Present
);
70 uint32_t VRDisplayCapabilities::MaxLayers() const {
71 return CanPresent() ? 1 : 0;
74 void VRDisplay::UpdateDisplayClient(
75 already_AddRefed
<gfx::VRDisplayClient
> aClient
) {
76 mClient
= std::move(aClient
);
80 bool VRDisplay::RefreshVRDisplays(uint64_t aWindowId
) {
81 gfx::VRManagerChild
* vm
= gfx::VRManagerChild::Get();
82 return vm
&& vm
->RefreshVRDisplaysWithCallback(aWindowId
);
86 void VRDisplay::UpdateVRDisplays(nsTArray
<RefPtr
<VRDisplay
>>& aDisplays
,
87 nsPIDOMWindowInner
* aWindow
) {
88 nsTArray
<RefPtr
<VRDisplay
>> displays
;
90 gfx::VRManagerChild
* vm
= gfx::VRManagerChild::Get();
91 nsTArray
<RefPtr
<gfx::VRDisplayClient
>> updatedDisplays
;
93 vm
->GetVRDisplays(updatedDisplays
);
94 for (size_t i
= 0; i
< updatedDisplays
.Length(); i
++) {
95 RefPtr
<gfx::VRDisplayClient
> display
= updatedDisplays
[i
];
96 bool isNewDisplay
= true;
97 for (size_t j
= 0; j
< aDisplays
.Length(); j
++) {
98 if (aDisplays
[j
]->GetClient()->GetDisplayInfo().GetDisplayID() ==
99 display
->GetDisplayInfo().GetDisplayID()) {
100 displays
.AppendElement(aDisplays
[j
]);
101 isNewDisplay
= false;
103 RefPtr
<gfx::VRDisplayClient
> ref
= display
;
104 aDisplays
[j
]->UpdateDisplayClient(do_AddRef(display
));
105 displays
.AppendElement(aDisplays
[j
]);
106 isNewDisplay
= false;
111 displays
.AppendElement(new VRDisplay(aWindow
, display
));
116 aDisplays
= std::move(displays
);
119 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView
, mParent
)
120 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfView
, AddRef
)
121 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfView
, Release
)
123 JSObject
* VRFieldOfView::WrapObject(JSContext
* aCx
,
124 JS::Handle
<JSObject
*> aGivenProto
) {
125 return VRFieldOfView_Binding::Wrap(aCx
, this, aGivenProto
);
128 NS_IMPL_CYCLE_COLLECTION_CLASS(VREyeParameters
)
130 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VREyeParameters
)
131 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
, mFOV
)
132 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
133 tmp
->mOffset
= nullptr;
134 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
136 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VREyeParameters
)
137 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
, mFOV
)
138 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
140 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VREyeParameters
)
141 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
142 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOffset
)
143 NS_IMPL_CYCLE_COLLECTION_TRACE_END
145 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VREyeParameters
, AddRef
)
146 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VREyeParameters
, Release
)
148 VREyeParameters::VREyeParameters(nsISupports
* aParent
,
149 const gfx::Point3D
& aEyeTranslation
,
150 const gfx::VRFieldOfView
& aFOV
,
151 const gfx::IntSize
& aRenderSize
)
153 mEyeTranslation(aEyeTranslation
),
154 mRenderSize(aRenderSize
) {
155 mFOV
= new VRFieldOfView(aParent
, aFOV
);
156 mozilla::HoldJSObjects(this);
159 VREyeParameters::~VREyeParameters() { mozilla::DropJSObjects(this); }
161 VRFieldOfView
* VREyeParameters::FieldOfView() { return mFOV
; }
163 void VREyeParameters::GetOffset(JSContext
* aCx
,
164 JS::MutableHandle
<JSObject
*> aRetval
,
167 // Lazily create the Float32Array
169 dom::Float32Array::Create(aCx
, this, 3, mEyeTranslation
.components
);
171 aRv
.NoteJSContextException(aCx
);
175 aRetval
.set(mOffset
);
178 JSObject
* VREyeParameters::WrapObject(JSContext
* aCx
,
179 JS::Handle
<JSObject
*> aGivenProto
) {
180 return VREyeParameters_Binding::Wrap(aCx
, this, aGivenProto
);
183 VRStageParameters::VRStageParameters(
184 nsISupports
* aParent
, const gfx::Matrix4x4
& aSittingToStandingTransform
,
185 const gfx::Size
& aSize
)
187 mSittingToStandingTransform(aSittingToStandingTransform
),
188 mSittingToStandingTransformArray(nullptr),
190 mozilla::HoldJSObjects(this);
193 VRStageParameters::~VRStageParameters() { mozilla::DropJSObjects(this); }
195 JSObject
* VRStageParameters::WrapObject(JSContext
* aCx
,
196 JS::Handle
<JSObject
*> aGivenProto
) {
197 return VRStageParameters_Binding::Wrap(aCx
, this, aGivenProto
);
200 NS_IMPL_CYCLE_COLLECTION_CLASS(VRStageParameters
)
202 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRStageParameters
)
203 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
)
204 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
205 tmp
->mSittingToStandingTransformArray
= nullptr;
206 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
208 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRStageParameters
)
209 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
)
210 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
212 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRStageParameters
)
213 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
214 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(
215 mSittingToStandingTransformArray
)
216 NS_IMPL_CYCLE_COLLECTION_TRACE_END
218 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRStageParameters
, AddRef
)
219 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRStageParameters
, Release
)
221 void VRStageParameters::GetSittingToStandingTransform(
222 JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aRetval
, ErrorResult
& aRv
) {
223 if (!mSittingToStandingTransformArray
) {
224 // Lazily create the Float32Array
225 mSittingToStandingTransformArray
= dom::Float32Array::Create(
226 aCx
, this, 16, mSittingToStandingTransform
.components
);
227 if (!mSittingToStandingTransformArray
) {
228 aRv
.NoteJSContextException(aCx
);
232 aRetval
.set(mSittingToStandingTransformArray
);
235 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDisplayCapabilities
, mParent
)
236 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRDisplayCapabilities
, AddRef
)
237 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRDisplayCapabilities
, Release
)
239 JSObject
* VRDisplayCapabilities::WrapObject(JSContext
* aCx
,
240 JS::Handle
<JSObject
*> aGivenProto
) {
241 return VRDisplayCapabilities_Binding::Wrap(aCx
, this, aGivenProto
);
244 VRPose::VRPose(nsISupports
* aParent
, const gfx::VRHMDSensorState
& aState
)
245 : Pose(aParent
), mVRState(aState
) {
246 mozilla::HoldJSObjects(this);
249 VRPose::VRPose(nsISupports
* aParent
) : Pose(aParent
) {
250 mVRState
.inputFrameID
= 0;
251 mVRState
.timestamp
= 0.0;
252 mVRState
.flags
= gfx::VRDisplayCapabilityFlags::Cap_None
;
253 mozilla::HoldJSObjects(this);
256 VRPose::~VRPose() { mozilla::DropJSObjects(this); }
258 void VRPose::GetPosition(JSContext
* aCx
, JS::MutableHandle
<JSObject
*> aRetval
,
261 bool(mVRState
.flags
& gfx::VRDisplayCapabilityFlags::Cap_Position
) ||
262 bool(mVRState
.flags
&
263 gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated
);
264 SetFloat32Array(aCx
, this, aRetval
, mPosition
,
265 valid
? mVRState
.pose
.position
: nullptr, 3, aRv
);
268 void VRPose::GetLinearVelocity(JSContext
* aCx
,
269 JS::MutableHandle
<JSObject
*> aRetval
,
272 bool(mVRState
.flags
& gfx::VRDisplayCapabilityFlags::Cap_Position
) ||
273 bool(mVRState
.flags
&
274 gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated
);
275 SetFloat32Array(aCx
, this, aRetval
, mLinearVelocity
,
276 valid
? mVRState
.pose
.linearVelocity
: nullptr, 3, aRv
);
279 void VRPose::GetLinearAcceleration(JSContext
* aCx
,
280 JS::MutableHandle
<JSObject
*> aRetval
,
282 const bool valid
= bool(
283 mVRState
.flags
& gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration
);
284 SetFloat32Array(aCx
, this, aRetval
, mLinearAcceleration
,
285 valid
? mVRState
.pose
.linearAcceleration
: nullptr, 3, aRv
);
288 void VRPose::GetOrientation(JSContext
* aCx
,
289 JS::MutableHandle
<JSObject
*> aRetval
,
292 bool(mVRState
.flags
& gfx::VRDisplayCapabilityFlags::Cap_Orientation
);
293 SetFloat32Array(aCx
, this, aRetval
, mOrientation
,
294 valid
? mVRState
.pose
.orientation
: nullptr, 4, aRv
);
297 void VRPose::GetAngularVelocity(JSContext
* aCx
,
298 JS::MutableHandle
<JSObject
*> aRetval
,
301 bool(mVRState
.flags
& gfx::VRDisplayCapabilityFlags::Cap_Orientation
);
302 SetFloat32Array(aCx
, this, aRetval
, mAngularVelocity
,
303 valid
? mVRState
.pose
.angularVelocity
: nullptr, 3, aRv
);
306 void VRPose::GetAngularAcceleration(JSContext
* aCx
,
307 JS::MutableHandle
<JSObject
*> aRetval
,
309 const bool valid
= bool(
310 mVRState
.flags
& gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration
);
311 SetFloat32Array(aCx
, this, aRetval
, mAngularAcceleration
,
312 valid
? mVRState
.pose
.angularAcceleration
: nullptr, 3, aRv
);
315 void VRPose::Update(const gfx::VRHMDSensorState
& aState
) { mVRState
= aState
; }
317 JSObject
* VRPose::WrapObject(JSContext
* aCx
,
318 JS::Handle
<JSObject
*> aGivenProto
) {
319 return VRPose_Binding::Wrap(aCx
, this, aGivenProto
);
323 JSObject
* VRDisplay::WrapObject(JSContext
* aCx
,
324 JS::Handle
<JSObject
*> aGivenProto
) {
325 return VRDisplay_Binding::Wrap(aCx
, this, aGivenProto
);
328 VRDisplay::VRDisplay(nsPIDOMWindowInner
* aWindow
, gfx::VRDisplayClient
* aClient
)
329 : DOMEventTargetHelper(aWindow
),
331 mDepthNear(0.01f
) // Default value from WebVR Spec
333 mDepthFar(10000.0f
) // Default value from WebVR Spec
335 mVRNavigationEventDepth(0),
337 const gfx::VRDisplayInfo
& info
= aClient
->GetDisplayInfo();
338 mCapabilities
= new VRDisplayCapabilities(aWindow
, info
.GetCapabilities());
339 if (info
.GetCapabilities() &
340 gfx::VRDisplayCapabilityFlags::Cap_StageParameters
) {
341 mStageParameters
= new VRStageParameters(
342 aWindow
, info
.GetSittingToStandingTransform(), info
.GetStageSize());
344 mozilla::HoldJSObjects(this);
345 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
346 if (MOZ_LIKELY(obs
)) {
347 obs
->AddObserver(this, "inner-window-destroyed", false);
351 VRDisplay::~VRDisplay() {
352 MOZ_ASSERT(mShutdown
);
353 mozilla::DropJSObjects(this);
356 void VRDisplay::LastRelease() {
357 // We don't want to wait for the CC to free up the presentation
358 // for use in other documents, so we do this in LastRelease().
362 already_AddRefed
<VREyeParameters
> VRDisplay::GetEyeParameters(VREye aEye
) {
363 gfx::VRDisplayState::Eye eye
= aEye
== VREye::Left
364 ? gfx::VRDisplayState::Eye_Left
365 : gfx::VRDisplayState::Eye_Right
;
366 RefPtr
<VREyeParameters
> params
= new VREyeParameters(
367 GetParentObject(), mClient
->GetDisplayInfo().GetEyeTranslation(eye
),
368 mClient
->GetDisplayInfo().GetEyeFOV(eye
),
369 mClient
->GetDisplayInfo().SuggestedEyeResolution());
370 return params
.forget();
373 VRDisplayCapabilities
* VRDisplay::Capabilities() { return mCapabilities
; }
375 VRStageParameters
* VRDisplay::GetStageParameters() { return mStageParameters
; }
377 uint32_t VRDisplay::DisplayId() const {
378 const gfx::VRDisplayInfo
& info
= mClient
->GetDisplayInfo();
379 return info
.GetDisplayID();
382 void VRDisplay::GetDisplayName(nsAString
& aDisplayName
) const {
383 const gfx::VRDisplayInfo
& info
= mClient
->GetDisplayInfo();
384 CopyUTF8toUTF16(MakeStringSpan(info
.GetDisplayName()), aDisplayName
);
387 void VRDisplay::UpdateFrameInfo() {
389 * The WebVR 1.1 spec Requires that VRDisplay.getPose and
390 * VRDisplay.getFrameData must return the same values until the next
391 * VRDisplay.submitFrame.
393 * mFrameInfo is marked dirty at the end of the frame or start of a new
394 * composition and lazily created here in order to receive mid-frame
395 * pose-prediction updates while still ensuring conformance to the WebVR spec
398 * If we are not presenting WebVR content, the frame will never end and we
399 * should return the latest frame data always.
403 if ((mFrameInfo
.IsDirty() && IsPresenting()) ||
404 mClient
->GetDisplayInfo().GetPresentingGroups() == 0) {
405 const gfx::VRHMDSensorState
& state
= mClient
->GetSensorState();
406 const gfx::VRDisplayInfo
& info
= mClient
->GetDisplayInfo();
407 mFrameInfo
.Update(info
, state
, mDepthNear
, mDepthFar
);
411 bool VRDisplay::GetFrameData(VRFrameData
& aFrameData
) {
413 if (!(mFrameInfo
.mVRState
.flags
&
414 gfx::VRDisplayCapabilityFlags::Cap_Orientation
)) {
415 // We must have at minimum Cap_Orientation for a valid pose.
418 aFrameData
.Update(mFrameInfo
);
422 already_AddRefed
<VRPose
> VRDisplay::GetPose() {
424 RefPtr
<VRPose
> obj
= new VRPose(GetParentObject(), mFrameInfo
.mVRState
);
429 void VRDisplay::ResetPose() {
430 // ResetPose is deprecated and unimplemented
431 // We must keep this stub function around as its referenced by
432 // VRDisplay.webidl. Not asserting here, as that could break existing web
436 void VRDisplay::StartVRNavigation() { mClient
->StartVRNavigation(); }
438 void VRDisplay::StartHandlingVRNavigationEvent() {
439 mHandlingVRNavigationEventStart
= TimeStamp::Now();
440 ++mVRNavigationEventDepth
;
441 TimeDuration timeout
=
442 TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
443 // A 0 or negative TimeDuration indicates that content may take
444 // as long as it wishes to respond to the event, as long as
445 // it happens before the event exits.
446 if (timeout
.ToMilliseconds() > 0) {
447 mClient
->StopVRNavigation(timeout
);
451 void VRDisplay::StopHandlingVRNavigationEvent() {
452 MOZ_ASSERT(mVRNavigationEventDepth
> 0);
453 --mVRNavigationEventDepth
;
454 if (mVRNavigationEventDepth
== 0) {
455 mClient
->StopVRNavigation(TimeDuration::FromMilliseconds(0));
459 bool VRDisplay::IsHandlingVRNavigationEvent() {
460 if (mVRNavigationEventDepth
== 0) {
463 if (mHandlingVRNavigationEventStart
.IsNull()) {
466 TimeDuration timeout
=
467 TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
468 return timeout
.ToMilliseconds() <= 0 ||
469 (TimeStamp::Now() - mHandlingVRNavigationEventStart
) <= timeout
;
472 void VRDisplay::OnPresentationGenerationChanged() { ExitPresentInternal(); }
474 already_AddRefed
<Promise
> VRDisplay::RequestPresent(
475 const nsTArray
<VRLayer
>& aLayers
, CallerType aCallerType
,
477 nsCOMPtr
<nsIGlobalObject
> global
= GetParentObject();
479 aRv
.Throw(NS_ERROR_FAILURE
);
483 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
484 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
486 bool isChromePresentation
= aCallerType
== CallerType::System
;
487 uint32_t presentationGroup
=
488 isChromePresentation
? gfx::kVRGroupChrome
: gfx::kVRGroupContent
;
490 mClient
->SetXRAPIMode(gfx::VRAPIMode::WebVR
);
491 if (!UserActivation::IsHandlingUserInput() && !isChromePresentation
&&
492 !IsHandlingVRNavigationEvent() && StaticPrefs::dom_vr_require_gesture() &&
494 // The WebVR API states that if called outside of a user gesture, the
495 // promise must be rejected. We allow VR presentations to start within
496 // trusted events such as vrdisplayactivate, which triggers in response to
497 // HMD proximity sensors and when navigating within a VR presentation.
498 // This user gesture requirement is not enforced for chrome/system code.
499 promise
->MaybeRejectWithUndefined();
500 } else if (!IsPresenting() && IsAnyPresenting(presentationGroup
)) {
501 // Only one presentation allowed per VRDisplay on a
502 // first-come-first-serve basis.
503 // If this Javascript context is presenting, then we can replace our
504 // presentation with a new one containing new layers but we should never
505 // replace the presentation of another context.
506 // Simultaneous presentations in other groups are allowed in separate
507 // Javascript contexts to enable browser UI from chrome/system contexts.
508 // Eventually, this restriction will be loosened to enable multitasking
510 promise
->MaybeRejectWithUndefined();
513 mPresentation
->UpdateLayers(aLayers
);
515 mPresentation
= mClient
->BeginPresentation(aLayers
, presentationGroup
);
518 promise
->MaybeResolve(JS::UndefinedHandleValue
);
520 return promise
.forget();
524 VRDisplay::Observe(nsISupports
* aSubject
, const char* aTopic
,
525 const char16_t
* aData
) {
526 MOZ_ASSERT(NS_IsMainThread());
528 if (strcmp(aTopic
, "inner-window-destroyed") == 0) {
529 nsCOMPtr
<nsISupportsPRUint64
> wrapper
= do_QueryInterface(aSubject
);
530 NS_ENSURE_TRUE(wrapper
, NS_ERROR_FAILURE
);
533 nsresult rv
= wrapper
->GetData(&innerID
);
534 NS_ENSURE_SUCCESS(rv
, rv
);
536 if (!GetOwner() || GetOwner()->WindowID() == innerID
) {
543 // This should not happen.
544 return NS_ERROR_FAILURE
;
547 already_AddRefed
<Promise
> VRDisplay::ExitPresent(ErrorResult
& aRv
) {
548 nsCOMPtr
<nsIGlobalObject
> global
= GetParentObject();
550 aRv
.Throw(NS_ERROR_FAILURE
);
554 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
555 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
557 if (!IsPresenting()) {
558 // We can not exit a presentation outside of the context that
559 // started the presentation.
560 promise
->MaybeRejectWithUndefined();
562 promise
->MaybeResolve(JS::UndefinedHandleValue
);
563 ExitPresentInternal();
566 return promise
.forget();
569 void VRDisplay::ExitPresentInternal() { mPresentation
= nullptr; }
571 void VRDisplay::Shutdown() {
573 ExitPresentInternal();
574 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
575 if (MOZ_LIKELY(obs
)) {
576 obs
->RemoveObserver(this, "inner-window-destroyed");
580 void VRDisplay::GetLayers(nsTArray
<VRLayer
>& result
) {
582 mPresentation
->GetDOMLayers(result
);
584 result
= nsTArray
<VRLayer
>();
588 void VRDisplay::SubmitFrame() {
589 AUTO_PROFILER_TRACING_MARKER("VR", "SubmitFrameAtVRDisplay", OTHER
);
591 if (mClient
&& !mClient
->IsPresentationGenerationCurrent()) {
592 mPresentation
= nullptr;
593 mClient
->MakePresentationGenerationCurrent();
597 mPresentation
->SubmitFrame();
602 int32_t VRDisplay::RequestAnimationFrame(FrameRequestCallback
& aCallback
,
603 ErrorResult
& aError
) {
608 gfx::VRManagerChild
* vm
= gfx::VRManagerChild::Get();
611 aError
= vm
->ScheduleFrameRequestCallback(aCallback
, &handle
);
615 void VRDisplay::CancelAnimationFrame(int32_t aHandle
, ErrorResult
& aError
) {
616 gfx::VRManagerChild
* vm
= gfx::VRManagerChild::Get();
617 vm
->CancelFrameRequestCallback(aHandle
);
620 bool VRDisplay::IsPresenting() const {
621 // IsPresenting returns true only if this Javascript context is presenting
622 // and will return false if another context is presenting.
623 return mPresentation
!= nullptr;
626 bool VRDisplay::IsAnyPresenting(uint32_t aGroupMask
) const {
627 // IsAnyPresenting returns true if either this VRDisplay object or any other
628 // from anther Javascript context is presenting with a group matching
630 if (mPresentation
&& (mPresentation
->GetGroup() & aGroupMask
)) {
633 if (mClient
->GetDisplayInfo().GetPresentingGroups() & aGroupMask
) {
639 bool VRDisplay::IsConnected() const { return mClient
->GetIsConnected(); }
641 uint32_t VRDisplay::PresentingGroups() const {
642 return mClient
->GetDisplayInfo().GetPresentingGroups();
645 uint32_t VRDisplay::GroupMask() const {
646 return mClient
->GetDisplayInfo().GetGroupMask();
649 void VRDisplay::SetGroupMask(const uint32_t& aGroupMask
) {
650 mClient
->SetGroupMask(aGroupMask
);
653 NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay
, DOMEventTargetHelper
,
654 mCapabilities
, mStageParameters
)
656 NS_IMPL_ADDREF_INHERITED(VRDisplay
, DOMEventTargetHelper
)
657 NS_IMPL_RELEASE_INHERITED(VRDisplay
, DOMEventTargetHelper
)
659 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VRDisplay
)
660 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
661 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, DOMEventTargetHelper
)
662 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
664 NS_IMPL_CYCLE_COLLECTION_CLASS(VRFrameData
)
666 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRFrameData
)
667 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
, mPose
)
668 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
669 tmp
->mLeftProjectionMatrix
= nullptr;
670 tmp
->mLeftViewMatrix
= nullptr;
671 tmp
->mRightProjectionMatrix
= nullptr;
672 tmp
->mRightViewMatrix
= nullptr;
673 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
675 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRFrameData
)
676 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
, mPose
)
677 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
679 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRFrameData
)
680 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
681 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftProjectionMatrix
)
682 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftViewMatrix
)
683 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightProjectionMatrix
)
684 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightViewMatrix
)
685 NS_IMPL_CYCLE_COLLECTION_TRACE_END
687 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFrameData
, AddRef
)
688 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFrameData
, Release
)
690 VRFrameData::VRFrameData(nsISupports
* aParent
)
692 mLeftProjectionMatrix(nullptr),
693 mLeftViewMatrix(nullptr),
694 mRightProjectionMatrix(nullptr),
695 mRightViewMatrix(nullptr) {
696 mozilla::HoldJSObjects(this);
697 mPose
= new VRPose(aParent
);
700 VRFrameData::~VRFrameData() { mozilla::DropJSObjects(this); }
703 already_AddRefed
<VRFrameData
> VRFrameData::Constructor(
704 const GlobalObject
& aGlobal
) {
705 RefPtr
<VRFrameData
> obj
= new VRFrameData(aGlobal
.GetAsSupports());
709 JSObject
* VRFrameData::WrapObject(JSContext
* aCx
,
710 JS::Handle
<JSObject
*> aGivenProto
) {
711 return VRFrameData_Binding::Wrap(aCx
, this, aGivenProto
);
714 VRPose
* VRFrameData::Pose() { return mPose
; }
716 double VRFrameData::Timestamp() const {
717 // Converting from seconds to milliseconds
718 return mFrameInfo
.mVRState
.timestamp
* 1000.0f
;
721 void VRFrameData::GetLeftProjectionMatrix(JSContext
* aCx
,
722 JS::MutableHandle
<JSObject
*> aRetval
,
724 Pose::SetFloat32Array(aCx
, this, aRetval
, mLeftProjectionMatrix
,
725 mFrameInfo
.mLeftProjection
.components
, 16, aRv
);
728 void VRFrameData::GetLeftViewMatrix(JSContext
* aCx
,
729 JS::MutableHandle
<JSObject
*> aRetval
,
731 Pose::SetFloat32Array(aCx
, this, aRetval
, mLeftViewMatrix
,
732 mFrameInfo
.mLeftView
.components
, 16, aRv
);
735 void VRFrameData::GetRightProjectionMatrix(JSContext
* aCx
,
736 JS::MutableHandle
<JSObject
*> aRetval
,
738 Pose::SetFloat32Array(aCx
, this, aRetval
, mRightProjectionMatrix
,
739 mFrameInfo
.mRightProjection
.components
, 16, aRv
);
742 void VRFrameData::GetRightViewMatrix(JSContext
* aCx
,
743 JS::MutableHandle
<JSObject
*> aRetval
,
745 Pose::SetFloat32Array(aCx
, this, aRetval
, mRightViewMatrix
,
746 mFrameInfo
.mRightView
.components
, 16, aRv
);
749 void VRFrameData::Update(const VRFrameInfo
& aFrameInfo
) {
750 mFrameInfo
= aFrameInfo
;
751 mPose
->Update(mFrameInfo
.mVRState
);
754 void VRFrameInfo::Update(const gfx::VRDisplayInfo
& aInfo
,
755 const gfx::VRHMDSensorState
& aState
, float aDepthNear
,
758 if (mTimeStampOffset
== 0.0f
) {
760 * A mTimeStampOffset value of 0.0f indicates that this is the first
761 * iteration and an offset has not yet been set.
763 * Generate a value for mTimeStampOffset such that if aState.timestamp is
764 * monotonically increasing, aState.timestamp + mTimeStampOffset will never
765 * be a negative number and will start at a pseudo-random offset
766 * between 1000.0f and 11000.0f seconds.
768 * We use a pseudo random offset rather than 0.0f just to discourage users
769 * from making the assumption that the timestamp returned in the WebVR API
770 * has a base of 0, which is not necessarily true in all UA's.
773 float(rand()) / float(RAND_MAX
) * 10000.0f
+ 1000.0f
- aState
.timestamp
;
775 mVRState
.timestamp
= aState
.timestamp
+ mTimeStampOffset
;
777 // Avoid division by zero within ConstructProjectionMatrix
778 const float kEpsilon
= 0.00001f
;
779 if (fabs(aDepthFar
- aDepthNear
) < kEpsilon
) {
780 aDepthFar
= aDepthNear
+ kEpsilon
;
783 const gfx::VRFieldOfView leftFOV
=
784 aInfo
.mDisplayState
.eyeFOV
[gfx::VRDisplayState::Eye_Left
];
786 leftFOV
.ConstructProjectionMatrix(aDepthNear
, aDepthFar
, true);
787 const gfx::VRFieldOfView rightFOV
=
788 aInfo
.mDisplayState
.eyeFOV
[gfx::VRDisplayState::Eye_Right
];
790 rightFOV
.ConstructProjectionMatrix(aDepthNear
, aDepthFar
, true);
791 memcpy(mLeftView
.components
, aState
.leftViewMatrix
,
792 sizeof(aState
.leftViewMatrix
));
793 memcpy(mRightView
.components
, aState
.rightViewMatrix
,
794 sizeof(aState
.rightViewMatrix
));
797 VRFrameInfo::VRFrameInfo() : mTimeStampOffset(0.0f
) {
798 mVRState
.inputFrameID
= 0;
799 mVRState
.timestamp
= 0.0;
800 mVRState
.flags
= gfx::VRDisplayCapabilityFlags::Cap_None
;
803 bool VRFrameInfo::IsDirty() { return mVRState
.timestamp
== 0; }
805 void VRFrameInfo::Clear() { mVRState
.Clear(); }
808 } // namespace mozilla