Source code

Revision control

Other Tools

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/. */
6
7
#include "nsWrapperCache.h"
8
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/StaticPrefs_dom.h"
18
#include "mozilla/gfx/DataSurfaceHelpers.h"
19
#include "Navigator.h"
20
#include "gfxUtils.h"
21
#include "gfxVR.h"
22
#include "VRDisplayClient.h"
23
#include "VRManagerChild.h"
24
#include "VRDisplayPresentation.h"
25
#include "nsIObserverService.h"
26
#include "nsIFrame.h"
27
#include "nsISupportsPrimitives.h"
28
29
using namespace mozilla::gfx;
30
31
namespace mozilla {
32
namespace dom {
33
34
VRFieldOfView::VRFieldOfView(nsISupports* aParent, double aUpDegrees,
35
double aRightDegrees, double aDownDegrees,
36
double aLeftDegrees)
37
: mParent(aParent),
38
mUpDegrees(aUpDegrees),
39
mRightDegrees(aRightDegrees),
40
mDownDegrees(aDownDegrees),
41
mLeftDegrees(aLeftDegrees) {}
42
43
VRFieldOfView::VRFieldOfView(nsISupports* aParent,
44
const gfx::VRFieldOfView& aSrc)
45
: mParent(aParent),
46
mUpDegrees(aSrc.upDegrees),
47
mRightDegrees(aSrc.rightDegrees),
48
mDownDegrees(aSrc.downDegrees),
49
mLeftDegrees(aSrc.leftDegrees) {}
50
51
bool VRDisplayCapabilities::HasPosition() const {
52
return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Position);
53
}
54
55
bool VRDisplayCapabilities::HasOrientation() const {
56
return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
57
}
58
59
bool VRDisplayCapabilities::HasExternalDisplay() const {
60
return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_External);
61
}
62
63
bool VRDisplayCapabilities::CanPresent() const {
64
return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Present);
65
}
66
67
uint32_t VRDisplayCapabilities::MaxLayers() const {
68
return CanPresent() ? 1 : 0;
69
}
70
71
void VRDisplay::UpdateDisplayClient(
72
already_AddRefed<gfx::VRDisplayClient> aClient) {
73
mClient = std::move(aClient);
74
}
75
76
/*static*/
77
bool VRDisplay::RefreshVRDisplays(uint64_t aWindowId) {
78
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
79
return vm && vm->RefreshVRDisplaysWithCallback(aWindowId);
80
}
81
82
/*static*/
83
void VRDisplay::UpdateVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays,
84
nsPIDOMWindowInner* aWindow) {
85
nsTArray<RefPtr<VRDisplay>> displays;
86
87
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
88
nsTArray<RefPtr<gfx::VRDisplayClient>> updatedDisplays;
89
if (vm) {
90
vm->GetVRDisplays(updatedDisplays);
91
for (size_t i = 0; i < updatedDisplays.Length(); i++) {
92
RefPtr<gfx::VRDisplayClient> display = updatedDisplays[i];
93
bool isNewDisplay = true;
94
for (size_t j = 0; j < aDisplays.Length(); j++) {
95
if (aDisplays[j]->GetClient()->GetDisplayInfo().GetDisplayID() ==
96
display->GetDisplayInfo().GetDisplayID()) {
97
displays.AppendElement(aDisplays[j]);
98
isNewDisplay = false;
99
} else {
100
RefPtr<gfx::VRDisplayClient> ref = display;
101
aDisplays[j]->UpdateDisplayClient(do_AddRef(display));
102
displays.AppendElement(aDisplays[j]);
103
isNewDisplay = false;
104
}
105
}
106
107
if (isNewDisplay) {
108
displays.AppendElement(new VRDisplay(aWindow, display));
109
}
110
}
111
}
112
113
aDisplays = displays;
114
}
115
116
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent)
117
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfView, AddRef)
118
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfView, Release)
119
120
JSObject* VRFieldOfView::WrapObject(JSContext* aCx,
121
JS::Handle<JSObject*> aGivenProto) {
122
return VRFieldOfView_Binding::Wrap(aCx, this, aGivenProto);
123
}
124
125
NS_IMPL_CYCLE_COLLECTION_CLASS(VREyeParameters)
126
127
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VREyeParameters)
128
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mFOV)
129
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
130
tmp->mOffset = nullptr;
131
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
132
133
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VREyeParameters)
134
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mFOV)
135
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
136
137
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VREyeParameters)
138
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
139
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOffset)
140
NS_IMPL_CYCLE_COLLECTION_TRACE_END
141
142
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VREyeParameters, AddRef)
143
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VREyeParameters, Release)
144
145
VREyeParameters::VREyeParameters(nsISupports* aParent,
146
const gfx::Point3D& aEyeTranslation,
147
const gfx::VRFieldOfView& aFOV,
148
const gfx::IntSize& aRenderSize)
149
: mParent(aParent),
150
mEyeTranslation(aEyeTranslation),
151
mRenderSize(aRenderSize) {
152
mFOV = new VRFieldOfView(aParent, aFOV);
153
mozilla::HoldJSObjects(this);
154
}
155
156
VREyeParameters::~VREyeParameters() { mozilla::DropJSObjects(this); }
157
158
VRFieldOfView* VREyeParameters::FieldOfView() { return mFOV; }
159
160
void VREyeParameters::GetOffset(JSContext* aCx,
161
JS::MutableHandle<JSObject*> aRetval,
162
ErrorResult& aRv) {
163
if (!mOffset) {
164
// Lazily create the Float32Array
165
mOffset =
166
dom::Float32Array::Create(aCx, this, 3, mEyeTranslation.components);
167
if (!mOffset) {
168
aRv.NoteJSContextException(aCx);
169
return;
170
}
171
}
172
aRetval.set(mOffset);
173
}
174
175
JSObject* VREyeParameters::WrapObject(JSContext* aCx,
176
JS::Handle<JSObject*> aGivenProto) {
177
return VREyeParameters_Binding::Wrap(aCx, this, aGivenProto);
178
}
179
180
VRStageParameters::VRStageParameters(
181
nsISupports* aParent, const gfx::Matrix4x4& aSittingToStandingTransform,
182
const gfx::Size& aSize)
183
: mParent(aParent),
184
mSittingToStandingTransform(aSittingToStandingTransform),
185
mSittingToStandingTransformArray(nullptr),
186
mSize(aSize) {
187
mozilla::HoldJSObjects(this);
188
}
189
190
VRStageParameters::~VRStageParameters() { mozilla::DropJSObjects(this); }
191
192
JSObject* VRStageParameters::WrapObject(JSContext* aCx,
193
JS::Handle<JSObject*> aGivenProto) {
194
return VRStageParameters_Binding::Wrap(aCx, this, aGivenProto);
195
}
196
197
NS_IMPL_CYCLE_COLLECTION_CLASS(VRStageParameters)
198
199
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRStageParameters)
200
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
201
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
202
tmp->mSittingToStandingTransformArray = nullptr;
203
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
204
205
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRStageParameters)
206
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
207
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
208
209
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRStageParameters)
210
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
211
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(
212
mSittingToStandingTransformArray)
213
NS_IMPL_CYCLE_COLLECTION_TRACE_END
214
215
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRStageParameters, AddRef)
216
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRStageParameters, Release)
217
218
void VRStageParameters::GetSittingToStandingTransform(
219
JSContext* aCx, JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) {
220
if (!mSittingToStandingTransformArray) {
221
// Lazily create the Float32Array
222
mSittingToStandingTransformArray = dom::Float32Array::Create(
223
aCx, this, 16, mSittingToStandingTransform.components);
224
if (!mSittingToStandingTransformArray) {
225
aRv.NoteJSContextException(aCx);
226
return;
227
}
228
}
229
aRetval.set(mSittingToStandingTransformArray);
230
}
231
232
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDisplayCapabilities, mParent)
233
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRDisplayCapabilities, AddRef)
234
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRDisplayCapabilities, Release)
235
236
JSObject* VRDisplayCapabilities::WrapObject(JSContext* aCx,
237
JS::Handle<JSObject*> aGivenProto) {
238
return VRDisplayCapabilities_Binding::Wrap(aCx, this, aGivenProto);
239
}
240
241
VRPose::VRPose(nsISupports* aParent, const gfx::VRHMDSensorState& aState)
242
: Pose(aParent), mVRState(aState) {
243
mozilla::HoldJSObjects(this);
244
}
245
246
VRPose::VRPose(nsISupports* aParent) : Pose(aParent) {
247
mVRState.inputFrameID = 0;
248
mVRState.timestamp = 0.0;
249
mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None;
250
mozilla::HoldJSObjects(this);
251
}
252
253
VRPose::~VRPose() { mozilla::DropJSObjects(this); }
254
255
void VRPose::GetPosition(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
256
ErrorResult& aRv) {
257
const bool valid =
258
bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
259
bool(mVRState.flags &
260
gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
261
SetFloat32Array(aCx, this, aRetval, mPosition,
262
valid ? mVRState.pose.position : nullptr, 3, aRv);
263
}
264
265
void VRPose::GetLinearVelocity(JSContext* aCx,
266
JS::MutableHandle<JSObject*> aRetval,
267
ErrorResult& aRv) {
268
const bool valid =
269
bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position);
270
SetFloat32Array(aCx, this, aRetval, mLinearVelocity,
271
valid ? mVRState.pose.linearVelocity : nullptr, 3, aRv);
272
}
273
274
void VRPose::GetLinearAcceleration(JSContext* aCx,
275
JS::MutableHandle<JSObject*> aRetval,
276
ErrorResult& aRv) {
277
const bool valid = bool(
278
mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration);
279
SetFloat32Array(aCx, this, aRetval, mLinearAcceleration,
280
valid ? mVRState.pose.linearAcceleration : nullptr, 3, aRv);
281
}
282
283
void VRPose::GetOrientation(JSContext* aCx,
284
JS::MutableHandle<JSObject*> aRetval,
285
ErrorResult& aRv) {
286
const bool valid =
287
bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
288
SetFloat32Array(aCx, this, aRetval, mOrientation,
289
valid ? mVRState.pose.orientation : nullptr, 4, aRv);
290
}
291
292
void VRPose::GetAngularVelocity(JSContext* aCx,
293
JS::MutableHandle<JSObject*> aRetval,
294
ErrorResult& aRv) {
295
const bool valid =
296
bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
297
SetFloat32Array(aCx, this, aRetval, mAngularVelocity,
298
valid ? mVRState.pose.angularVelocity : nullptr, 3, aRv);
299
}
300
301
void VRPose::GetAngularAcceleration(JSContext* aCx,
302
JS::MutableHandle<JSObject*> aRetval,
303
ErrorResult& aRv) {
304
const bool valid = bool(
305
mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration);
306
SetFloat32Array(aCx, this, aRetval, mAngularAcceleration,
307
valid ? mVRState.pose.angularAcceleration : nullptr, 3, aRv);
308
}
309
310
void VRPose::Update(const gfx::VRHMDSensorState& aState) { mVRState = aState; }
311
312
JSObject* VRPose::WrapObject(JSContext* aCx,
313
JS::Handle<JSObject*> aGivenProto) {
314
return VRPose_Binding::Wrap(aCx, this, aGivenProto);
315
}
316
317
/* virtual */
318
JSObject* VRDisplay::WrapObject(JSContext* aCx,
319
JS::Handle<JSObject*> aGivenProto) {
320
return VRDisplay_Binding::Wrap(aCx, this, aGivenProto);
321
}
322
323
VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient)
324
: DOMEventTargetHelper(aWindow),
325
mClient(aClient),
326
mDepthNear(0.01f) // Default value from WebVR Spec
327
,
328
mDepthFar(10000.0f) // Default value from WebVR Spec
329
,
330
mVRNavigationEventDepth(0),
331
mShutdown(false) {
332
const gfx::VRDisplayInfo& info = aClient->GetDisplayInfo();
333
mCapabilities = new VRDisplayCapabilities(aWindow, info.GetCapabilities());
334
if (info.GetCapabilities() &
335
gfx::VRDisplayCapabilityFlags::Cap_StageParameters) {
336
mStageParameters = new VRStageParameters(
337
aWindow, info.GetSittingToStandingTransform(), info.GetStageSize());
338
}
339
mozilla::HoldJSObjects(this);
340
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
341
if (MOZ_LIKELY(obs)) {
342
obs->AddObserver(this, "inner-window-destroyed", false);
343
}
344
}
345
346
VRDisplay::~VRDisplay() {
347
MOZ_ASSERT(mShutdown);
348
mozilla::DropJSObjects(this);
349
}
350
351
void VRDisplay::LastRelease() {
352
// We don't want to wait for the CC to free up the presentation
353
// for use in other documents, so we do this in LastRelease().
354
Shutdown();
355
}
356
357
already_AddRefed<VREyeParameters> VRDisplay::GetEyeParameters(VREye aEye) {
358
gfx::VRDisplayState::Eye eye = aEye == VREye::Left
359
? gfx::VRDisplayState::Eye_Left
360
: gfx::VRDisplayState::Eye_Right;
361
RefPtr<VREyeParameters> params = new VREyeParameters(
362
GetParentObject(), mClient->GetDisplayInfo().GetEyeTranslation(eye),
363
mClient->GetDisplayInfo().GetEyeFOV(eye),
364
mClient->GetDisplayInfo().SuggestedEyeResolution());
365
return params.forget();
366
}
367
368
VRDisplayCapabilities* VRDisplay::Capabilities() { return mCapabilities; }
369
370
VRStageParameters* VRDisplay::GetStageParameters() { return mStageParameters; }
371
372
uint32_t VRDisplay::DisplayId() const {
373
const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
374
return info.GetDisplayID();
375
}
376
377
void VRDisplay::GetDisplayName(nsAString& aDisplayName) const {
378
const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
379
aDisplayName = NS_ConvertUTF8toUTF16(info.GetDisplayName());
380
}
381
382
void VRDisplay::UpdateFrameInfo() {
383
/**
384
* The WebVR 1.1 spec Requires that VRDisplay.getPose and
385
* VRDisplay.getFrameData must return the same values until the next
386
* VRDisplay.submitFrame.
387
*
388
* mFrameInfo is marked dirty at the end of the frame or start of a new
389
* composition and lazily created here in order to receive mid-frame
390
* pose-prediction updates while still ensuring conformance to the WebVR spec
391
* requirements.
392
*
393
* If we are not presenting WebVR content, the frame will never end and we
394
* should return the latest frame data always.
395
*/
396
mFrameInfo.Clear();
397
398
if ((mFrameInfo.IsDirty() && IsPresenting()) ||
399
mClient->GetDisplayInfo().GetPresentingGroups() == 0) {
400
const gfx::VRHMDSensorState& state = mClient->GetSensorState();
401
const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
402
mFrameInfo.Update(info, state, mDepthNear, mDepthFar);
403
}
404
}
405
406
bool VRDisplay::GetFrameData(VRFrameData& aFrameData) {
407
UpdateFrameInfo();
408
if (!(mFrameInfo.mVRState.flags &
409
gfx::VRDisplayCapabilityFlags::Cap_Orientation)) {
410
// We must have at minimum Cap_Orientation for a valid pose.
411
return false;
412
}
413
aFrameData.Update(mFrameInfo);
414
return true;
415
}
416
417
already_AddRefed<VRPose> VRDisplay::GetPose() {
418
UpdateFrameInfo();
419
RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState);
420
421
return obj.forget();
422
}
423
424
void VRDisplay::ResetPose() {
425
// ResetPose is deprecated and unimplemented
426
// We must keep this stub function around as its referenced by
427
// VRDisplay.webidl. Not asserting here, as that could break existing web
428
// content.
429
}
430
431
void VRDisplay::StartVRNavigation() { mClient->StartVRNavigation(); }
432
433
void VRDisplay::StartHandlingVRNavigationEvent() {
434
mHandlingVRNavigationEventStart = TimeStamp::Now();
435
++mVRNavigationEventDepth;
436
TimeDuration timeout =
437
TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
438
// A 0 or negative TimeDuration indicates that content may take
439
// as long as it wishes to respond to the event, as long as
440
// it happens before the event exits.
441
if (timeout.ToMilliseconds() > 0) {
442
mClient->StopVRNavigation(timeout);
443
}
444
}
445
446
void VRDisplay::StopHandlingVRNavigationEvent() {
447
MOZ_ASSERT(mVRNavigationEventDepth > 0);
448
--mVRNavigationEventDepth;
449
if (mVRNavigationEventDepth == 0) {
450
mClient->StopVRNavigation(TimeDuration::FromMilliseconds(0));
451
}
452
}
453
454
bool VRDisplay::IsHandlingVRNavigationEvent() {
455
if (mVRNavigationEventDepth == 0) {
456
return false;
457
}
458
if (mHandlingVRNavigationEventStart.IsNull()) {
459
return false;
460
}
461
TimeDuration timeout =
462
TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
463
return timeout.ToMilliseconds() <= 0 ||
464
(TimeStamp::Now() - mHandlingVRNavigationEventStart) <= timeout;
465
}
466
467
void VRDisplay::OnPresentationGenerationChanged() { ExitPresentInternal(); }
468
469
already_AddRefed<Promise> VRDisplay::RequestPresent(
470
const nsTArray<VRLayer>& aLayers, CallerType aCallerType,
471
ErrorResult& aRv) {
472
nsCOMPtr<nsIGlobalObject> global = GetParentObject();
473
if (!global) {
474
aRv.Throw(NS_ERROR_FAILURE);
475
return nullptr;
476
}
477
478
RefPtr<Promise> promise = Promise::Create(global, aRv);
479
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
480
481
bool isChromePresentation = aCallerType == CallerType::System;
482
uint32_t presentationGroup =
483
isChromePresentation ? gfx::kVRGroupChrome : gfx::kVRGroupContent;
484
485
if (!UserActivation::IsHandlingUserInput() && !isChromePresentation &&
486
!IsHandlingVRNavigationEvent() && StaticPrefs::dom_vr_require_gesture() &&
487
!IsPresenting()) {
488
// The WebVR API states that if called outside of a user gesture, the
489
// promise must be rejected. We allow VR presentations to start within
490
// trusted events such as vrdisplayactivate, which triggers in response to
491
// HMD proximity sensors and when navigating within a VR presentation.
492
// This user gesture requirement is not enforced for chrome/system code.
493
promise->MaybeRejectWithUndefined();
494
} else if (!IsPresenting() && IsAnyPresenting(presentationGroup)) {
495
// Only one presentation allowed per VRDisplay on a
496
// first-come-first-serve basis.
497
// If this Javascript context is presenting, then we can replace our
498
// presentation with a new one containing new layers but we should never
499
// replace the presentation of another context.
500
// Simultaneous presentations in other groups are allowed in separate
501
// Javascript contexts to enable browser UI from chrome/system contexts.
502
// Eventually, this restriction will be loosened to enable multitasking
503
// use cases.
504
promise->MaybeRejectWithUndefined();
505
} else {
506
if (mPresentation) {
507
mPresentation->UpdateLayers(aLayers);
508
} else {
509
mPresentation = mClient->BeginPresentation(aLayers, presentationGroup);
510
}
511
mFrameInfo.Clear();
512
promise->MaybeResolve(JS::UndefinedHandleValue);
513
}
514
return promise.forget();
515
}
516
517
NS_IMETHODIMP
518
VRDisplay::Observe(nsISupports* aSubject, const char* aTopic,
519
const char16_t* aData) {
520
MOZ_ASSERT(NS_IsMainThread());
521
522
if (strcmp(aTopic, "inner-window-destroyed") == 0) {
523
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
524
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
525
526
uint64_t innerID;
527
nsresult rv = wrapper->GetData(&innerID);
528
NS_ENSURE_SUCCESS(rv, rv);
529
530
if (!GetOwner() || GetOwner()->WindowID() == innerID) {
531
Shutdown();
532
}
533
534
return NS_OK;
535
}
536
537
// This should not happen.
538
return NS_ERROR_FAILURE;
539
}
540
541
already_AddRefed<Promise> VRDisplay::ExitPresent(ErrorResult& aRv) {
542
nsCOMPtr<nsIGlobalObject> global = GetParentObject();
543
if (!global) {
544
aRv.Throw(NS_ERROR_FAILURE);
545
return nullptr;
546
}
547
548
RefPtr<Promise> promise = Promise::Create(global, aRv);
549
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
550
551
if (!IsPresenting()) {
552
// We can not exit a presentation outside of the context that
553
// started the presentation.
554
promise->MaybeRejectWithUndefined();
555
} else {
556
promise->MaybeResolve(JS::UndefinedHandleValue);
557
ExitPresentInternal();
558
}
559
560
return promise.forget();
561
}
562
563
void VRDisplay::ExitPresentInternal() { mPresentation = nullptr; }
564
565
void VRDisplay::Shutdown() {
566
mShutdown = true;
567
ExitPresentInternal();
568
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
569
if (MOZ_LIKELY(obs)) {
570
obs->RemoveObserver(this, "inner-window-destroyed");
571
}
572
}
573
574
void VRDisplay::GetLayers(nsTArray<VRLayer>& result) {
575
if (mPresentation) {
576
mPresentation->GetDOMLayers(result);
577
} else {
578
result = nsTArray<VRLayer>();
579
}
580
}
581
582
void VRDisplay::SubmitFrame() {
583
AUTO_PROFILER_TRACING("VR", "SubmitFrameAtVRDisplay", OTHER);
584
585
if (mClient && !mClient->IsPresentationGenerationCurrent()) {
586
mPresentation = nullptr;
587
mClient->MakePresentationGenerationCurrent();
588
}
589
590
if (mPresentation) {
591
mPresentation->SubmitFrame();
592
}
593
mFrameInfo.Clear();
594
}
595
596
int32_t VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
597
ErrorResult& aError) {
598
if (mShutdown) {
599
return 0;
600
}
601
602
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
603
604
int32_t handle;
605
aError = vm->ScheduleFrameRequestCallback(aCallback, &handle);
606
return handle;
607
}
608
609
void VRDisplay::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) {
610
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
611
vm->CancelFrameRequestCallback(aHandle);
612
}
613
614
bool VRDisplay::IsPresenting() const {
615
// IsPresenting returns true only if this Javascript context is presenting
616
// and will return false if another context is presenting.
617
return mPresentation != nullptr;
618
}
619
620
bool VRDisplay::IsAnyPresenting(uint32_t aGroupMask) const {
621
// IsAnyPresenting returns true if either this VRDisplay object or any other
622
// from anther Javascript context is presenting with a group matching
623
// aGroupMask.
624
if (mPresentation && (mPresentation->GetGroup() & aGroupMask)) {
625
return true;
626
}
627
if (mClient->GetDisplayInfo().GetPresentingGroups() & aGroupMask) {
628
return true;
629
}
630
return false;
631
}
632
633
bool VRDisplay::IsConnected() const { return mClient->GetIsConnected(); }
634
635
uint32_t VRDisplay::PresentingGroups() const {
636
return mClient->GetDisplayInfo().GetPresentingGroups();
637
}
638
639
uint32_t VRDisplay::GroupMask() const {
640
return mClient->GetDisplayInfo().GetGroupMask();
641
}
642
643
void VRDisplay::SetGroupMask(const uint32_t& aGroupMask) {
644
mClient->SetGroupMask(aGroupMask);
645
}
646
647
NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper,
648
mCapabilities, mStageParameters)
649
650
NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper)
651
NS_IMPL_RELEASE_INHERITED(VRDisplay, DOMEventTargetHelper)
652
653
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VRDisplay)
654
NS_INTERFACE_MAP_ENTRY(nsIObserver)
655
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, DOMEventTargetHelper)
656
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
657
658
NS_IMPL_CYCLE_COLLECTION_CLASS(VRFrameData)
659
660
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRFrameData)
661
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPose)
662
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
663
tmp->mLeftProjectionMatrix = nullptr;
664
tmp->mLeftViewMatrix = nullptr;
665
tmp->mRightProjectionMatrix = nullptr;
666
tmp->mRightViewMatrix = nullptr;
667
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
668
669
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRFrameData)
670
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPose)
671
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
672
673
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRFrameData)
674
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
675
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftProjectionMatrix)
676
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftViewMatrix)
677
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightProjectionMatrix)
678
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightViewMatrix)
679
NS_IMPL_CYCLE_COLLECTION_TRACE_END
680
681
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFrameData, AddRef)
682
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFrameData, Release)
683
684
VRFrameData::VRFrameData(nsISupports* aParent)
685
: mParent(aParent),
686
mLeftProjectionMatrix(nullptr),
687
mLeftViewMatrix(nullptr),
688
mRightProjectionMatrix(nullptr),
689
mRightViewMatrix(nullptr) {
690
mozilla::HoldJSObjects(this);
691
mPose = new VRPose(aParent);
692
}
693
694
VRFrameData::~VRFrameData() { mozilla::DropJSObjects(this); }
695
696
/* static */
697
already_AddRefed<VRFrameData> VRFrameData::Constructor(
698
const GlobalObject& aGlobal) {
699
RefPtr<VRFrameData> obj = new VRFrameData(aGlobal.GetAsSupports());
700
return obj.forget();
701
}
702
703
JSObject* VRFrameData::WrapObject(JSContext* aCx,
704
JS::Handle<JSObject*> aGivenProto) {
705
return VRFrameData_Binding::Wrap(aCx, this, aGivenProto);
706
}
707
708
VRPose* VRFrameData::Pose() { return mPose; }
709
710
double VRFrameData::Timestamp() const {
711
// Converting from seconds to milliseconds
712
return mFrameInfo.mVRState.timestamp * 1000.0f;
713
}
714
715
void VRFrameData::GetLeftProjectionMatrix(JSContext* aCx,
716
JS::MutableHandle<JSObject*> aRetval,
717
ErrorResult& aRv) {
718
Pose::SetFloat32Array(aCx, this, aRetval, mLeftProjectionMatrix,
719
mFrameInfo.mLeftProjection.components, 16, aRv);
720
}
721
722
void VRFrameData::GetLeftViewMatrix(JSContext* aCx,
723
JS::MutableHandle<JSObject*> aRetval,
724
ErrorResult& aRv) {
725
Pose::SetFloat32Array(aCx, this, aRetval, mLeftViewMatrix,
726
mFrameInfo.mLeftView.components, 16, aRv);
727
}
728
729
void VRFrameData::GetRightProjectionMatrix(JSContext* aCx,
730
JS::MutableHandle<JSObject*> aRetval,
731
ErrorResult& aRv) {
732
Pose::SetFloat32Array(aCx, this, aRetval, mRightProjectionMatrix,
733
mFrameInfo.mRightProjection.components, 16, aRv);
734
}
735
736
void VRFrameData::GetRightViewMatrix(JSContext* aCx,
737
JS::MutableHandle<JSObject*> aRetval,
738
ErrorResult& aRv) {
739
Pose::SetFloat32Array(aCx, this, aRetval, mRightViewMatrix,
740
mFrameInfo.mRightView.components, 16, aRv);
741
}
742
743
void VRFrameData::Update(const VRFrameInfo& aFrameInfo) {
744
mFrameInfo = aFrameInfo;
745
mPose->Update(mFrameInfo.mVRState);
746
}
747
748
void VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo,
749
const gfx::VRHMDSensorState& aState, float aDepthNear,
750
float aDepthFar) {
751
mVRState = aState;
752
if (mTimeStampOffset == 0.0f) {
753
/**
754
* A mTimeStampOffset value of 0.0f indicates that this is the first
755
* iteration and an offset has not yet been set.
756
*
757
* Generate a value for mTimeStampOffset such that if aState.timestamp is
758
* monotonically increasing, aState.timestamp + mTimeStampOffset will never
759
* be a negative number and will start at a pseudo-random offset
760
* between 1000.0f and 11000.0f seconds.
761
*
762
* We use a pseudo random offset rather than 0.0f just to discourage users
763
* from making the assumption that the timestamp returned in the WebVR API
764
* has a base of 0, which is not necessarily true in all UA's.
765
*/
766
mTimeStampOffset =
767
float(rand()) / float(RAND_MAX) * 10000.0f + 1000.0f - aState.timestamp;
768
}
769
mVRState.timestamp = aState.timestamp + mTimeStampOffset;
770
771
// Avoid division by zero within ConstructProjectionMatrix
772
const float kEpsilon = 0.00001f;
773
if (fabs(aDepthFar - aDepthNear) < kEpsilon) {
774
aDepthFar = aDepthNear + kEpsilon;
775
}
776
777
const gfx::VRFieldOfView leftFOV =
778
aInfo.mDisplayState.eyeFOV[gfx::VRDisplayState::Eye_Left];
779
mLeftProjection =
780
leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
781
const gfx::VRFieldOfView rightFOV =
782
aInfo.mDisplayState.eyeFOV[gfx::VRDisplayState::Eye_Right];
783
mRightProjection =
784
rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
785
memcpy(mLeftView.components, aState.leftViewMatrix,
786
sizeof(aState.leftViewMatrix));
787
memcpy(mRightView.components, aState.rightViewMatrix,
788
sizeof(aState.rightViewMatrix));
789
}
790
791
VRFrameInfo::VRFrameInfo() : mTimeStampOffset(0.0f) {
792
mVRState.inputFrameID = 0;
793
mVRState.timestamp = 0.0;
794
mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None;
795
}
796
797
bool VRFrameInfo::IsDirty() { return mVRState.timestamp == 0; }
798
799
void VRFrameInfo::Clear() { mVRState.Clear(); }
800
801
} // namespace dom
802
} // namespace mozilla