summaryrefslogtreecommitdiffstats
path: root/dom/vr/XRSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/vr/XRSystem.cpp703
1 files changed, 703 insertions, 0 deletions
diff --git a/dom/vr/XRSystem.cpp b/dom/vr/XRSystem.cpp
new file mode 100644
index 0000000000..97795ac7cd
--- /dev/null
+++ b/dom/vr/XRSystem.cpp
@@ -0,0 +1,703 @@
+/* -*- 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"
+
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/dom/XRPermissionRequest.h"
+#include "mozilla/dom/XRSession.h"
+#include "mozilla/dom/BindingCallContext.h"
+#include "mozilla/dom/Document.h"
+#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::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);
+
+ // 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();
+ }
+
+ if (immersive || aOptions.mRequiredFeatures.WasPassed() ||
+ aOptions.mOptionalFeatures.WasPassed()) {
+ if (!responsibleDocument->HasValidTransientUserGestureActivation() &&
+ 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::Rooted<JS::Value> 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::Rooted<JS::Value> 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;
+ }
+
+ bool isChromeSession = aCallerType == CallerType::System;
+ uint32_t presentationGroup =
+ isChromeSession ? gfx::kVRGroupChrome : gfx::kVRGroupContent;
+ RefPtr<RequestSessionRequest> request = new RequestSessionRequest(
+ aMode, presentationGroup, promise, requiredReferenceSpaceTypes,
+ optionalReferenceSpaceTypes);
+ 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(
+ std::move(mRequestSessionRequestsWaitingForEnumeration));
+ 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());
+}
+
+bool XRSystem::HasActiveImmersiveSession() const {
+ return mActiveImmersiveSession;
+}
+
+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(
+ std::move(mRequestSessionRequestsWithoutHardware));
+
+ 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(
+ std::move(mRequestSessionRequestsWaitingForEnumeration));
+
+ 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(
+ GetOwner(), this, display, request->GetPresentationGroup(),
+ enabledReferenceSpaceTypes);
+ 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(
+ std::move(mIsSessionSupportedRequests));
+ 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(
+ std::move(mRequestSessionRequestsWaitingForRuntimeDetection));
+
+ 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) {}
+void XRSystem::NotifyPresentationGenerationChanged(uint32_t aDisplayID) {
+ if (mActiveImmersiveSession) {
+ mActiveImmersiveSession->ExitPresent();
+ }
+}
+bool XRSystem::GetStopActivityStatus() const { return true; }
+
+RequestSessionRequest::RequestSessionRequest(
+ XRSessionMode aSessionMode, uint32_t aPresentationGroup, Promise* aPromise,
+ const nsTArray<XRReferenceSpaceType>& aRequiredReferenceSpaceTypes,
+ const nsTArray<XRReferenceSpaceType>& aOptionalReferenceSpaceTypes)
+ : mPromise(aPromise),
+ mSessionMode(aSessionMode),
+ mPresentationGroup(aPresentationGroup),
+ mRequiredReferenceSpaceTypes(aRequiredReferenceSpaceTypes.Clone()),
+ mOptionalReferenceSpaceTypes(aOptionalReferenceSpaceTypes.Clone()) {}
+
+bool RequestSessionRequest::ResolveSupport(
+ const gfx::VRDisplayClient* aDisplay,
+ nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes) const {
+ if (aDisplay) {
+ if (!aDisplay->GetIsConnected()) {
+ return false;
+ }
+ if ((aDisplay->GetDisplayInfo().GetPresentingGroups() &
+ mPresentationGroup) != 0) {
+ return false;
+ }
+
+ 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;
+}
+
+uint32_t RequestSessionRequest::GetPresentationGroup() const {
+ return mPresentationGroup;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// IsSessionSupportedRequest cycle collection
+NS_IMPL_CYCLE_COLLECTION(IsSessionSupportedRequest, mPromise)
+
+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::Handle<JS::Value> 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)
+
+} // namespace mozilla::dom