2020-04-06 19:33:32 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "mozilla/dom/XRSystem.h"
|
2020-11-23 16:21:38 +00:00
|
|
|
|
|
|
|
#include "mozilla/StaticPrefs_dom.h"
|
2020-04-06 19:33:32 +00:00
|
|
|
#include "mozilla/dom/XRPermissionRequest.h"
|
|
|
|
#include "mozilla/dom/XRSession.h"
|
|
|
|
#include "mozilla/dom/BindingCallContext.h"
|
2020-11-23 16:21:38 +00:00
|
|
|
#include "mozilla/dom/Document.h"
|
2020-04-06 19:33:32 +00:00
|
|
|
#include "mozilla/dom/Promise.h"
|
|
|
|
#include "mozilla/dom/FeaturePolicyUtils.h"
|
|
|
|
#include "mozilla/dom/PermissionMessageUtils.h"
|
|
|
|
#include "nsGlobalWindow.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "gfxVR.h"
|
|
|
|
#include "VRDisplayClient.h"
|
|
|
|
#include "VRManagerChild.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
using namespace gfx;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// XRSystem cycle collection
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(XRSystem)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XRSystem,
|
|
|
|
DOMEventTargetHelper)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveImmersiveSession)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSessions)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIsSessionSupportedRequests)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
|
|
|
|
mRequestSessionRequestsWaitingForRuntimeDetection)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequestSessionRequestsWithoutHardware)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
|
|
|
|
mRequestSessionRequestsWaitingForEnumeration)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XRSystem, DOMEventTargetHelper)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveImmersiveSession)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSessions)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIsSessionSupportedRequests)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(
|
|
|
|
mRequestSessionRequestsWaitingForRuntimeDetection)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequestSessionRequestsWithoutHardware)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequestSessionRequestsWaitingForEnumeration)
|
|
|
|
// Don't need NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER because
|
|
|
|
// DOMEventTargetHelper does it for us.
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XRSystem, DOMEventTargetHelper)
|
|
|
|
|
|
|
|
JSObject* XRSystem::WrapObject(JSContext* aCx,
|
|
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
|
|
return XRSystem_Binding::Wrap(aCx, this, aGivenProto);
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
already_AddRefed<XRSystem> XRSystem::Create(nsPIDOMWindowInner* aWindow) {
|
|
|
|
MOZ_ASSERT(aWindow);
|
|
|
|
|
|
|
|
RefPtr<XRSystem> service = new XRSystem(aWindow);
|
|
|
|
return service.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
XRSystem::XRSystem(nsPIDOMWindowInner* aWindow)
|
|
|
|
: DOMEventTargetHelper(aWindow),
|
|
|
|
mShuttingDown(false),
|
|
|
|
mPendingImmersiveSession(false),
|
|
|
|
mEnumerationInFlight(false) {
|
|
|
|
// Unregister with VRManagerChild
|
|
|
|
VRManagerChild* vmc = VRManagerChild::Get();
|
|
|
|
if (vmc) {
|
|
|
|
vmc->AddListener(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::Shutdown() {
|
|
|
|
MOZ_ASSERT(!mShuttingDown);
|
|
|
|
mShuttingDown = true;
|
|
|
|
|
|
|
|
// Unregister from VRManagerChild
|
|
|
|
if (VRManagerChild::IsCreated()) {
|
|
|
|
VRManagerChild* vmc = VRManagerChild::Get();
|
|
|
|
vmc->RemoveListener(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::SessionEnded(XRSession* aSession) {
|
|
|
|
if (mActiveImmersiveSession == aSession) {
|
|
|
|
mActiveImmersiveSession = nullptr;
|
|
|
|
}
|
|
|
|
mInlineSessions.RemoveElement(aSession);
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Promise> XRSystem::IsSessionSupported(XRSessionMode aMode,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = GetParentObject();
|
|
|
|
NS_ENSURE_TRUE(global, nullptr);
|
|
|
|
|
|
|
|
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
|
|
|
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
|
|
|
|
|
|
|
if (aMode == XRSessionMode::Inline) {
|
|
|
|
promise->MaybeResolve(true);
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mIsSessionSupportedRequests.IsEmpty()) {
|
|
|
|
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
|
|
|
|
vm->DetectRuntimes();
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<IsSessionSupportedRequest> request =
|
|
|
|
new IsSessionSupportedRequest(aMode, promise);
|
|
|
|
mIsSessionSupportedRequests.AppendElement(request);
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Promise> XRSystem::RequestSession(
|
|
|
|
JSContext* aCx, XRSessionMode aMode, const XRSessionInit& aOptions,
|
|
|
|
CallerType aCallerType, ErrorResult& aRv) {
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = GetParentObject();
|
|
|
|
NS_ENSURE_TRUE(global, nullptr);
|
|
|
|
|
|
|
|
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
|
|
|
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
|
|
|
|
|
|
|
bool immersive = (aMode == XRSessionMode::Immersive_vr ||
|
|
|
|
aMode == XRSessionMode::Immersive_ar);
|
|
|
|
|
2020-06-02 19:55:45 +00:00
|
|
|
// The document must be a responsible document, active and focused.
|
|
|
|
nsCOMPtr<Document> responsibleDocument = GetDocumentIfCurrent();
|
|
|
|
if (!responsibleDocument) {
|
|
|
|
// The document is not trustworthy
|
|
|
|
promise->MaybeRejectWithSecurityError("This document is not responsible.");
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:33:32 +00:00
|
|
|
if (immersive || aOptions.mRequiredFeatures.WasPassed() ||
|
|
|
|
aOptions.mOptionalFeatures.WasPassed()) {
|
2020-06-02 19:55:45 +00:00
|
|
|
if (!responsibleDocument->HasValidTransientUserGestureActivation() &&
|
2020-04-06 19:33:32 +00:00
|
|
|
aCallerType != CallerType::System &&
|
|
|
|
StaticPrefs::dom_vr_require_gesture()) {
|
|
|
|
// A user gesture is required.
|
|
|
|
promise->MaybeRejectWithSecurityError("A user gesture is required.");
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsTArray<XRReferenceSpaceType> requiredReferenceSpaceTypes;
|
|
|
|
nsTArray<XRReferenceSpaceType> optionalReferenceSpaceTypes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* By default, all sessions will require XRReferenceSpaceType::Viewer
|
|
|
|
* and immersive sessions will require XRReferenceSpaceType::Local.
|
|
|
|
*
|
|
|
|
* https://www.w3.org/TR/webxr/#default-features
|
|
|
|
*/
|
|
|
|
requiredReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Viewer);
|
|
|
|
if (immersive) {
|
|
|
|
requiredReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Local);
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingCallContext callCx(aCx, "XRSystem.requestSession");
|
|
|
|
|
|
|
|
if (aOptions.mRequiredFeatures.WasPassed()) {
|
|
|
|
const Sequence<JS::Value>& arr = (aOptions.mRequiredFeatures.Value());
|
|
|
|
for (const JS::Value& val : arr) {
|
|
|
|
if (!val.isNull() && !val.isUndefined()) {
|
|
|
|
bool bFound = false;
|
|
|
|
JS::RootedValue v(aCx, val);
|
|
|
|
int index = 0;
|
|
|
|
if (FindEnumStringIndex<false>(
|
|
|
|
callCx, v, XRReferenceSpaceTypeValues::strings,
|
|
|
|
"XRReferenceSpaceType", "Argument 2 of XR.requestSession",
|
|
|
|
&index)) {
|
|
|
|
if (index >= 0) {
|
|
|
|
requiredReferenceSpaceTypes.AppendElement(
|
|
|
|
static_cast<XRReferenceSpaceType>(index));
|
|
|
|
bFound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!bFound) {
|
|
|
|
promise->MaybeRejectWithNotSupportedError(
|
|
|
|
"A required feature for the XRSession is not available.");
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aOptions.mOptionalFeatures.WasPassed()) {
|
|
|
|
const Sequence<JS::Value>& arr = (aOptions.mOptionalFeatures.Value());
|
|
|
|
for (const JS::Value& val : arr) {
|
|
|
|
if (!val.isNull() && !val.isUndefined()) {
|
|
|
|
JS::RootedValue v(aCx, val);
|
|
|
|
int index = 0;
|
|
|
|
if (FindEnumStringIndex<false>(
|
|
|
|
callCx, v, XRReferenceSpaceTypeValues::strings,
|
|
|
|
"XRReferenceSpaceType", "Argument 2 of XR.requestSession",
|
|
|
|
&index)) {
|
|
|
|
if (index >= 0) {
|
|
|
|
optionalReferenceSpaceTypes.AppendElement(
|
|
|
|
static_cast<XRReferenceSpaceType>(index));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (immersive) {
|
|
|
|
if (mPendingImmersiveSession || mActiveImmersiveSession) {
|
|
|
|
promise->MaybeRejectWithInvalidStateError(
|
|
|
|
"There can only be one immersive XRSession.");
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
mPendingImmersiveSession = true;
|
|
|
|
}
|
|
|
|
|
2020-04-09 01:22:52 +00:00
|
|
|
bool isChromeSession = aCallerType == CallerType::System;
|
|
|
|
uint32_t presentationGroup =
|
|
|
|
isChromeSession ? gfx::kVRGroupChrome : gfx::kVRGroupContent;
|
2020-04-06 19:33:32 +00:00
|
|
|
RefPtr<RequestSessionRequest> request = new RequestSessionRequest(
|
2020-04-09 01:22:52 +00:00
|
|
|
aMode, presentationGroup, promise, requiredReferenceSpaceTypes,
|
|
|
|
optionalReferenceSpaceTypes);
|
2020-04-06 19:33:32 +00:00
|
|
|
if (request->WantsHardware()) {
|
|
|
|
QueueSessionRequestWithEnumeration(request);
|
|
|
|
} else {
|
|
|
|
QueueSessionRequestWithoutEnumeration(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::QueueSessionRequestWithEnumeration(
|
|
|
|
RequestSessionRequest* aRequest) {
|
|
|
|
MOZ_ASSERT(aRequest->WantsHardware());
|
|
|
|
mRequestSessionRequestsWaitingForRuntimeDetection.AppendElement(aRequest);
|
|
|
|
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
|
|
|
|
vm->DetectRuntimes();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::QueueSessionRequestWithoutEnumeration(
|
|
|
|
RequestSessionRequest* aRequest) {
|
|
|
|
MOZ_ASSERT(!aRequest->NeedsHardware());
|
|
|
|
mRequestSessionRequestsWithoutHardware.AppendElement(aRequest);
|
|
|
|
|
|
|
|
ResolveSessionRequestsWithoutHardware();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XRSystem::CancelHardwareRequest(RequestSessionRequest* aRequest) {
|
|
|
|
if (!aRequest->NeedsHardware()) {
|
|
|
|
// If hardware access was an optional requirement and the user
|
|
|
|
// opted not to provide access, queue the request
|
|
|
|
// to be resolved without hardware.
|
|
|
|
QueueSessionRequestWithoutEnumeration(aRequest);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aRequest->IsImmersive()) {
|
|
|
|
mPendingImmersiveSession = false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XRSystem::OnXRPermissionRequestAllow() {
|
|
|
|
if (!gfx::VRManagerChild::IsCreated()) {
|
|
|
|
// It's possible that this callback returns after
|
|
|
|
// we have already started shutting down.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!mEnumerationInFlight) {
|
|
|
|
mEnumerationInFlight = true;
|
|
|
|
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
|
|
|
|
Unused << vm->EnumerateVRDisplays();
|
|
|
|
}
|
|
|
|
return mEnumerationInFlight ||
|
|
|
|
!mRequestSessionRequestsWaitingForEnumeration.IsEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::OnXRPermissionRequestCancel() {
|
|
|
|
nsTArray<RefPtr<RequestSessionRequest>> requestSessionRequests(
|
2020-05-06 11:46:55 +00:00
|
|
|
std::move(mRequestSessionRequestsWaitingForEnumeration));
|
2020-04-06 19:33:32 +00:00
|
|
|
for (RefPtr<RequestSessionRequest>& request : requestSessionRequests) {
|
|
|
|
if (CancelHardwareRequest(request)) {
|
|
|
|
request->mPromise->MaybeRejectWithSecurityError(
|
|
|
|
"A device supporting the requested session "
|
|
|
|
"configuration could not be found.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XRSystem::FeaturePolicyBlocked() const {
|
|
|
|
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(GetOwner());
|
|
|
|
if (!win) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
RefPtr<XRPermissionRequest> request =
|
|
|
|
new XRPermissionRequest(win, win->WindowID());
|
|
|
|
return !(request->CheckPermissionDelegate());
|
|
|
|
}
|
|
|
|
|
2020-04-07 18:48:13 +00:00
|
|
|
bool XRSystem::HasActiveImmersiveSession() const {
|
|
|
|
return mActiveImmersiveSession;
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:33:32 +00:00
|
|
|
void XRSystem::ResolveSessionRequestsWithoutHardware() {
|
|
|
|
// Resolve promises returned by RequestSession
|
|
|
|
nsTArray<RefPtr<gfx::VRDisplayClient>> displays;
|
|
|
|
// Try resolving support without a device, for inline sessions.
|
|
|
|
displays.AppendElement(nullptr);
|
|
|
|
|
|
|
|
nsTArray<RefPtr<RequestSessionRequest>> requestSessionRequests(
|
2020-05-06 11:46:55 +00:00
|
|
|
std::move(mRequestSessionRequestsWithoutHardware));
|
2020-04-06 19:33:32 +00:00
|
|
|
|
|
|
|
ResolveSessionRequests(requestSessionRequests, displays);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::NotifyEnumerationCompleted() {
|
|
|
|
// Enumeration has completed.
|
|
|
|
mEnumerationInFlight = false;
|
|
|
|
|
|
|
|
if (!gfx::VRManagerChild::IsCreated()) {
|
|
|
|
// It's possible that this callback returns after
|
|
|
|
// we have already started shutting down.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resolve promises returned by RequestSession
|
|
|
|
nsTArray<RefPtr<gfx::VRDisplayClient>> displays;
|
|
|
|
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
|
|
|
|
vm->GetVRDisplays(displays);
|
|
|
|
|
|
|
|
nsTArray<RefPtr<RequestSessionRequest>> requestSessionRequests(
|
2020-05-06 11:46:55 +00:00
|
|
|
std::move(mRequestSessionRequestsWaitingForEnumeration));
|
2020-04-06 19:33:32 +00:00
|
|
|
|
|
|
|
ResolveSessionRequests(requestSessionRequests, displays);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::ResolveSessionRequests(
|
|
|
|
nsTArray<RefPtr<RequestSessionRequest>>& aRequests,
|
|
|
|
const nsTArray<RefPtr<gfx::VRDisplayClient>>& aDisplays) {
|
|
|
|
for (RefPtr<RequestSessionRequest>& request : aRequests) {
|
|
|
|
RefPtr<XRSession> session;
|
|
|
|
if (request->IsImmersive()) {
|
|
|
|
mPendingImmersiveSession = false;
|
|
|
|
}
|
|
|
|
// Select an XR device
|
|
|
|
for (const RefPtr<gfx::VRDisplayClient>& display : aDisplays) {
|
|
|
|
nsTArray<XRReferenceSpaceType> enabledReferenceSpaceTypes;
|
|
|
|
if (request->ResolveSupport(display, enabledReferenceSpaceTypes)) {
|
|
|
|
if (request->IsImmersive()) {
|
|
|
|
session = XRSession::CreateImmersiveSession(
|
2020-04-09 01:22:52 +00:00
|
|
|
GetOwner(), this, display, request->GetPresentationGroup(),
|
|
|
|
enabledReferenceSpaceTypes);
|
2020-04-06 19:33:32 +00:00
|
|
|
mActiveImmersiveSession = session;
|
|
|
|
} else {
|
|
|
|
session = XRSession::CreateInlineSession(GetOwner(), this,
|
|
|
|
enabledReferenceSpaceTypes);
|
|
|
|
mInlineSessions.AppendElement(session);
|
|
|
|
}
|
|
|
|
request->mPromise->MaybeResolve(session);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!session) {
|
|
|
|
request->mPromise->MaybeRejectWithNotSupportedError(
|
|
|
|
"A device supporting the required XRSession configuration "
|
|
|
|
"could not be found.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::NotifyDetectRuntimesCompleted() {
|
|
|
|
ResolveIsSessionSupportedRequests();
|
|
|
|
if (!mRequestSessionRequestsWaitingForRuntimeDetection.IsEmpty()) {
|
|
|
|
ProcessSessionRequestsWaitingForRuntimeDetection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::ResolveIsSessionSupportedRequests() {
|
|
|
|
// Resolve promises returned by IsSessionSupported
|
|
|
|
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
|
|
|
|
nsTArray<RefPtr<IsSessionSupportedRequest>> isSessionSupportedRequests(
|
2020-05-06 11:46:55 +00:00
|
|
|
std::move(mIsSessionSupportedRequests));
|
2020-04-06 19:33:32 +00:00
|
|
|
bool featurePolicyBlocked = FeaturePolicyBlocked();
|
|
|
|
|
|
|
|
for (RefPtr<IsSessionSupportedRequest>& request :
|
|
|
|
isSessionSupportedRequests) {
|
|
|
|
if (featurePolicyBlocked) {
|
|
|
|
request->mPromise->MaybeRejectWithSecurityError(
|
|
|
|
"The xr-spatial-tracking feature policy is required.");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool supported = false;
|
|
|
|
switch (request->GetSessionMode()) {
|
|
|
|
case XRSessionMode::Immersive_vr:
|
|
|
|
supported = vm->RuntimeSupportsVR();
|
|
|
|
break;
|
|
|
|
case XRSessionMode::Immersive_ar:
|
|
|
|
supported = vm->RuntimeSupportsAR();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
request->mPromise->MaybeResolve(supported);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::ProcessSessionRequestsWaitingForRuntimeDetection() {
|
|
|
|
bool alreadyRequestedPermission =
|
|
|
|
!mRequestSessionRequestsWaitingForEnumeration.IsEmpty();
|
|
|
|
bool featurePolicyBlocked = FeaturePolicyBlocked();
|
|
|
|
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
|
|
|
|
|
|
|
|
nsTArray<RefPtr<RequestSessionRequest>> sessionRequests(
|
2020-05-06 11:46:55 +00:00
|
|
|
std::move(mRequestSessionRequestsWaitingForRuntimeDetection));
|
2020-04-06 19:33:32 +00:00
|
|
|
|
|
|
|
for (RefPtr<RequestSessionRequest>& request : sessionRequests) {
|
|
|
|
bool compatibleRuntime = false;
|
|
|
|
switch (request->GetSessionMode()) {
|
|
|
|
case XRSessionMode::Immersive_vr:
|
|
|
|
compatibleRuntime = vm->RuntimeSupportsVR();
|
|
|
|
break;
|
|
|
|
case XRSessionMode::Immersive_ar:
|
|
|
|
compatibleRuntime = vm->RuntimeSupportsAR();
|
|
|
|
break;
|
|
|
|
case XRSessionMode::Inline:
|
|
|
|
compatibleRuntime = vm->RuntimeSupportsInline();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!compatibleRuntime) {
|
|
|
|
// If none of the requested sessions are supported by a
|
|
|
|
// runtime, early exit without showing a permission prompt.
|
|
|
|
if (CancelHardwareRequest(request)) {
|
|
|
|
request->mPromise->MaybeRejectWithNotSupportedError(
|
|
|
|
"A device supporting the required XRSession configuration "
|
|
|
|
"could not be found.");
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (featurePolicyBlocked) {
|
|
|
|
// Don't show a permission prompt if blocked by feature policy.
|
|
|
|
if (CancelHardwareRequest(request)) {
|
|
|
|
request->mPromise->MaybeRejectWithSecurityError(
|
|
|
|
"The xr-spatial-tracking feature policy is required.");
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// To continue evaluating this request, it must wait for hardware
|
|
|
|
// enumeration and permission request.
|
|
|
|
mRequestSessionRequestsWaitingForEnumeration.AppendElement(request);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mRequestSessionRequestsWaitingForEnumeration.IsEmpty() &&
|
|
|
|
!alreadyRequestedPermission) {
|
|
|
|
/**
|
|
|
|
* Inline sessions will require only a user gesture
|
|
|
|
* and should not trigger XR permission UI.
|
|
|
|
* This is not a problem currently, as the only platforms
|
|
|
|
* allowing xr-spatial-tracking for inline sessions do not
|
|
|
|
* present a modal XR permission UI. (eg. Android Firefox Reality)
|
|
|
|
*/
|
|
|
|
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(GetOwner());
|
|
|
|
win->RequestXRPermission();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::NotifyVRDisplayMounted(uint32_t aDisplayID) {}
|
|
|
|
void XRSystem::NotifyVRDisplayUnmounted(uint32_t aDisplayID) {}
|
|
|
|
|
|
|
|
void XRSystem::NotifyVRDisplayConnect(uint32_t aDisplayID) {
|
|
|
|
DispatchTrustedEvent(u"devicechange"_ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::NotifyVRDisplayDisconnect(uint32_t aDisplayID) {
|
|
|
|
DispatchTrustedEvent(u"devicechange"_ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XRSystem::NotifyVRDisplayPresentChange(uint32_t aDisplayID) {}
|
2020-04-07 18:48:13 +00:00
|
|
|
void XRSystem::NotifyPresentationGenerationChanged(uint32_t aDisplayID) {
|
|
|
|
if (mActiveImmersiveSession) {
|
|
|
|
mActiveImmersiveSession->ExitPresent();
|
|
|
|
}
|
|
|
|
}
|
2020-04-06 19:33:32 +00:00
|
|
|
bool XRSystem::GetStopActivityStatus() const { return true; }
|
|
|
|
|
|
|
|
RequestSessionRequest::RequestSessionRequest(
|
2020-04-09 01:22:52 +00:00
|
|
|
XRSessionMode aSessionMode, uint32_t aPresentationGroup, Promise* aPromise,
|
2020-04-06 19:33:32 +00:00
|
|
|
const nsTArray<XRReferenceSpaceType>& aRequiredReferenceSpaceTypes,
|
|
|
|
const nsTArray<XRReferenceSpaceType>& aOptionalReferenceSpaceTypes)
|
|
|
|
: mPromise(aPromise),
|
|
|
|
mSessionMode(aSessionMode),
|
2020-04-09 01:22:52 +00:00
|
|
|
mPresentationGroup(aPresentationGroup),
|
2020-05-06 11:46:55 +00:00
|
|
|
mRequiredReferenceSpaceTypes(aRequiredReferenceSpaceTypes.Clone()),
|
|
|
|
mOptionalReferenceSpaceTypes(aOptionalReferenceSpaceTypes.Clone()) {}
|
2020-04-06 19:33:32 +00:00
|
|
|
|
|
|
|
bool RequestSessionRequest::ResolveSupport(
|
|
|
|
const gfx::VRDisplayClient* aDisplay,
|
|
|
|
nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes) const {
|
|
|
|
if (aDisplay) {
|
|
|
|
if (!aDisplay->GetIsConnected()) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-09 01:22:52 +00:00
|
|
|
if ((aDisplay->GetDisplayInfo().GetPresentingGroups() &
|
|
|
|
mPresentationGroup) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-06 19:33:32 +00:00
|
|
|
|
|
|
|
const gfx::VRDisplayInfo& info = aDisplay->GetDisplayInfo();
|
|
|
|
switch (mSessionMode) {
|
|
|
|
case XRSessionMode::Inline:
|
|
|
|
if (!bool(info.mDisplayState.capabilityFlags &
|
|
|
|
gfx::VRDisplayCapabilityFlags::Cap_Inline)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XRSessionMode::Immersive_vr:
|
|
|
|
if (!bool(info.mDisplayState.capabilityFlags &
|
|
|
|
gfx::VRDisplayCapabilityFlags::Cap_ImmersiveVR)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XRSessionMode::Immersive_ar:
|
|
|
|
if (!bool(info.mDisplayState.capabilityFlags &
|
|
|
|
gfx::VRDisplayCapabilityFlags::Cap_ImmersiveAR)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (mSessionMode != XRSessionMode::Inline) {
|
|
|
|
// If we don't have a device, we can only support inline sessions
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All sessions support XRReferenceSpaceType::Viewer by default
|
|
|
|
aEnabledReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Viewer);
|
|
|
|
|
|
|
|
// Immersive sessions support XRReferenceSpaceType::Local by default
|
|
|
|
if (IsImmersive()) {
|
|
|
|
aEnabledReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Local);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (XRReferenceSpaceType type : mRequiredReferenceSpaceTypes) {
|
|
|
|
if (aDisplay) {
|
|
|
|
if (!aDisplay->IsReferenceSpaceTypeSupported(type)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (type != XRReferenceSpaceType::Viewer) {
|
|
|
|
// If we don't have a device, We only support
|
|
|
|
// XRReferenceSpaceType::Viewer
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!aEnabledReferenceSpaceTypes.Contains(type)) {
|
|
|
|
aEnabledReferenceSpaceTypes.AppendElement(type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aDisplay) {
|
|
|
|
for (XRReferenceSpaceType type : mOptionalReferenceSpaceTypes) {
|
|
|
|
if (aDisplay->IsReferenceSpaceTypeSupported(type) &&
|
|
|
|
!aEnabledReferenceSpaceTypes.Contains(type)) {
|
|
|
|
aEnabledReferenceSpaceTypes.AppendElement(type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RequestSessionRequest::IsImmersive() const {
|
|
|
|
return (mSessionMode == XRSessionMode::Immersive_vr ||
|
|
|
|
mSessionMode == XRSessionMode::Immersive_ar);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RequestSessionRequest::WantsHardware() const {
|
|
|
|
for (XRReferenceSpaceType type : mOptionalReferenceSpaceTypes) {
|
|
|
|
// Any XRReferenceSpaceType other than Viewer requires hardware
|
|
|
|
if (type != XRReferenceSpaceType::Viewer) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NeedsHardware();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RequestSessionRequest::NeedsHardware() const {
|
|
|
|
for (XRReferenceSpaceType type : mRequiredReferenceSpaceTypes) {
|
|
|
|
// Any XRReferenceSpaceType other than Viewer requires hardware
|
|
|
|
if (type != XRReferenceSpaceType::Viewer) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
XRSessionMode RequestSessionRequest::GetSessionMode() const {
|
|
|
|
return mSessionMode;
|
|
|
|
}
|
|
|
|
|
2020-04-09 01:22:52 +00:00
|
|
|
uint32_t RequestSessionRequest::GetPresentationGroup() const {
|
|
|
|
return mPresentationGroup;
|
|
|
|
}
|
|
|
|
|
2020-04-06 19:33:32 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// IsSessionSupportedRequest cycle collection
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(IsSessionSupportedRequest, mPromise)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(IsSessionSupportedRequest, AddRef)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(IsSessionSupportedRequest, Release)
|
|
|
|
|
|
|
|
XRSessionMode IsSessionSupportedRequest::GetSessionMode() const {
|
|
|
|
return mSessionMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// XRRequestSessionPermissionRequest cycle collection
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(XRRequestSessionPermissionRequest,
|
|
|
|
ContentPermissionRequestBase)
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(
|
|
|
|
XRRequestSessionPermissionRequest, ContentPermissionRequestBase)
|
|
|
|
|
|
|
|
XRRequestSessionPermissionRequest::XRRequestSessionPermissionRequest(
|
|
|
|
nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
|
|
|
|
AllowCallback&& aAllowCallback,
|
|
|
|
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
|
|
|
CancelCallback&& aCancelCallback)
|
|
|
|
: ContentPermissionRequestBase(aNodePrincipal, aWindow, "dom.xr"_ns,
|
|
|
|
"xr"_ns),
|
|
|
|
mAllowCallback(std::move(aAllowCallback)),
|
|
|
|
mAllowAnySiteCallback(std::move(aAllowAnySiteCallback)),
|
|
|
|
mCancelCallback(std::move(aCancelCallback)),
|
|
|
|
mCallbackCalled(false) {
|
|
|
|
mPermissionRequests.AppendElement(
|
|
|
|
PermissionRequest(mType, nsTArray<nsString>()));
|
|
|
|
}
|
|
|
|
|
|
|
|
XRRequestSessionPermissionRequest::~XRRequestSessionPermissionRequest() {
|
|
|
|
Cancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
XRRequestSessionPermissionRequest::Cancel() {
|
|
|
|
if (!mCallbackCalled) {
|
|
|
|
mCallbackCalled = true;
|
|
|
|
mCancelCallback();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
XRRequestSessionPermissionRequest::Allow(JS::HandleValue aChoices) {
|
|
|
|
nsTArray<PermissionChoice> choices;
|
|
|
|
nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// There is no support to allow grants automatically from the prompting code
|
|
|
|
// path.
|
|
|
|
|
|
|
|
if (!mCallbackCalled) {
|
|
|
|
mCallbackCalled = true;
|
|
|
|
if (choices.Length() == 1 &&
|
|
|
|
choices[0].choice().EqualsLiteral("allow-on-any-site")) {
|
|
|
|
mAllowAnySiteCallback();
|
|
|
|
} else if (choices.Length() == 1 &&
|
|
|
|
choices[0].choice().EqualsLiteral("allow")) {
|
|
|
|
mAllowCallback();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<XRRequestSessionPermissionRequest>
|
|
|
|
XRRequestSessionPermissionRequest::Create(
|
|
|
|
nsPIDOMWindowInner* aWindow, AllowCallback&& aAllowCallback,
|
|
|
|
AllowAnySiteCallback&& aAllowAnySiteCallback,
|
|
|
|
CancelCallback&& aCancelCallback) {
|
|
|
|
if (!aWindow) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(aWindow);
|
|
|
|
if (!win->GetPrincipal()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
RefPtr<XRRequestSessionPermissionRequest> request =
|
|
|
|
new XRRequestSessionPermissionRequest(
|
|
|
|
aWindow, win->GetPrincipal(), std::move(aAllowCallback),
|
|
|
|
std::move(aAllowAnySiteCallback), std::move(aCancelCallback));
|
|
|
|
return request.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// RequestSessionRequest cycle collection
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(RequestSessionRequest, mPromise)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(RequestSessionRequest, AddRef)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(RequestSessionRequest, Release)
|
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|