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