Coverage Report

Created: 2018-09-25 14:53

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