tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

XRSystem.cpp (23302B)


      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 "mozilla/dom/XRSystem.h"
      8 
      9 #include "VRDisplayClient.h"
     10 #include "VRManagerChild.h"
     11 #include "gfxVR.h"
     12 #include "mozilla/StaticPrefs_dom.h"
     13 #include "mozilla/dom/BindingCallContext.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/FeaturePolicyUtils.h"
     16 #include "mozilla/dom/PermissionMessageUtils.h"
     17 #include "mozilla/dom/Promise.h"
     18 #include "mozilla/dom/XRPermissionRequest.h"
     19 #include "mozilla/dom/XRSession.h"
     20 #include "nsGlobalWindowInner.h"
     21 #include "nsThreadUtils.h"
     22 
     23 namespace mozilla::dom {
     24 
     25 using namespace gfx;
     26 
     27 ////////////////////////////////////////////////////////////////////////////////
     28 // XRSystem cycle collection
     29 NS_IMPL_CYCLE_COLLECTION_CLASS(XRSystem)
     30 
     31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XRSystem,
     32                                                  DOMEventTargetHelper)
     33  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveImmersiveSession)
     34  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSessions)
     35  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIsSessionSupportedRequests)
     36  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
     37      mRequestSessionRequestsWaitingForRuntimeDetection)
     38  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequestSessionRequestsWithoutHardware)
     39  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
     40      mRequestSessionRequestsWaitingForEnumeration)
     41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     42 
     43 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XRSystem, DOMEventTargetHelper)
     44  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveImmersiveSession)
     45  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSessions)
     46  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIsSessionSupportedRequests)
     47  NS_IMPL_CYCLE_COLLECTION_UNLINK(
     48      mRequestSessionRequestsWaitingForRuntimeDetection)
     49  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequestSessionRequestsWithoutHardware)
     50  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequestSessionRequestsWaitingForEnumeration)
     51  // Don't need NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER because
     52  // DOMEventTargetHelper does it for us.
     53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     54 
     55 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XRSystem, DOMEventTargetHelper)
     56 
     57 JSObject* XRSystem::WrapObject(JSContext* aCx,
     58                               JS::Handle<JSObject*> aGivenProto) {
     59  return XRSystem_Binding::Wrap(aCx, this, aGivenProto);
     60 }
     61 
     62 // static
     63 already_AddRefed<XRSystem> XRSystem::Create(nsPIDOMWindowInner* aWindow) {
     64  MOZ_ASSERT(aWindow);
     65 
     66  RefPtr<XRSystem> service = new XRSystem(aWindow);
     67  return service.forget();
     68 }
     69 
     70 XRSystem::XRSystem(nsPIDOMWindowInner* aWindow)
     71    : DOMEventTargetHelper(aWindow),
     72      mShuttingDown(false),
     73      mPendingImmersiveSession(false),
     74      mEnumerationInFlight(false) {
     75  // Unregister with VRManagerChild
     76  VRManagerChild* vmc = VRManagerChild::Get();
     77  if (vmc) {
     78    vmc->AddListener(this);
     79  }
     80 }
     81 
     82 void XRSystem::Shutdown() {
     83  MOZ_ASSERT(!mShuttingDown);
     84  mShuttingDown = true;
     85 
     86  // Unregister from VRManagerChild
     87  if (VRManagerChild::IsCreated()) {
     88    VRManagerChild* vmc = VRManagerChild::Get();
     89    vmc->RemoveListener(this);
     90  }
     91 }
     92 
     93 void XRSystem::SessionEnded(XRSession* aSession) {
     94  if (mActiveImmersiveSession == aSession) {
     95    mActiveImmersiveSession = nullptr;
     96  }
     97  mInlineSessions.RemoveElement(aSession);
     98 }
     99 
    100 already_AddRefed<Promise> XRSystem::IsSessionSupported(XRSessionMode aMode,
    101                                                       ErrorResult& aRv) {
    102  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    103  NS_ENSURE_TRUE(global, nullptr);
    104 
    105  RefPtr<Promise> promise = Promise::Create(global, aRv);
    106  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    107 
    108  if (aMode == XRSessionMode::Inline) {
    109    promise->MaybeResolve(true);
    110    return promise.forget();
    111  }
    112 
    113  if (mIsSessionSupportedRequests.IsEmpty()) {
    114    gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    115    vm->DetectRuntimes();
    116  }
    117 
    118  RefPtr<IsSessionSupportedRequest> request =
    119      new IsSessionSupportedRequest(aMode, promise);
    120  mIsSessionSupportedRequests.AppendElement(request);
    121  return promise.forget();
    122 }
    123 
    124 already_AddRefed<Promise> XRSystem::RequestSession(
    125    XRSessionMode aMode, const XRSessionInit& aOptions, CallerType aCallerType,
    126    ErrorResult& aRv) {
    127  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    128  NS_ENSURE_TRUE(global, nullptr);
    129 
    130  RefPtr<Promise> promise = Promise::Create(global, aRv);
    131  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    132 
    133  bool immersive = (aMode == XRSessionMode::Immersive_vr ||
    134                    aMode == XRSessionMode::Immersive_ar);
    135 
    136  // The document must be a responsible document, active and focused.
    137  nsCOMPtr<Document> responsibleDocument = GetDocumentIfCurrent();
    138  if (!responsibleDocument) {
    139    // The document is not trustworthy
    140    promise->MaybeRejectWithSecurityError("This document is not responsible.");
    141    return promise.forget();
    142  }
    143 
    144  if (immersive || aOptions.mRequiredFeatures.WasPassed() ||
    145      aOptions.mOptionalFeatures.WasPassed()) {
    146    if (!responsibleDocument->HasValidTransientUserGestureActivation() &&
    147        aCallerType != CallerType::System &&
    148        StaticPrefs::dom_vr_require_gesture()) {
    149      // A user gesture is required.
    150      promise->MaybeRejectWithSecurityError("A user gesture is required.");
    151      return promise.forget();
    152    }
    153  }
    154 
    155  nsTArray<XRReferenceSpaceType> requiredReferenceSpaceTypes;
    156  nsTArray<XRReferenceSpaceType> optionalReferenceSpaceTypes;
    157 
    158  /**
    159   * By default, all sessions will require XRReferenceSpaceType::Viewer
    160   * and immersive sessions will require XRReferenceSpaceType::Local.
    161   *
    162   * https://www.w3.org/TR/webxr/#default-features
    163   */
    164  requiredReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Viewer);
    165  if (immersive) {
    166    requiredReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Local);
    167  }
    168 
    169  if (aOptions.mRequiredFeatures.WasPassed()) {
    170    for (const nsString& val : aOptions.mRequiredFeatures.Value()) {
    171      Maybe<XRReferenceSpaceType> type =
    172          StringToEnum<XRReferenceSpaceType>(val);
    173      if (type.isNothing()) {
    174        promise->MaybeRejectWithNotSupportedError(
    175            "A required feature for the XRSession is not available.");
    176        return promise.forget();
    177      }
    178      requiredReferenceSpaceTypes.AppendElement(type.value());
    179    }
    180  }
    181 
    182  if (aOptions.mOptionalFeatures.WasPassed()) {
    183    for (const nsString& val : aOptions.mOptionalFeatures.Value()) {
    184      Maybe<XRReferenceSpaceType> type =
    185          StringToEnum<XRReferenceSpaceType>(val);
    186      if (type.isSome()) {
    187        optionalReferenceSpaceTypes.AppendElement(type.value());
    188      }
    189    }
    190  }
    191 
    192  if (immersive) {
    193    if (mPendingImmersiveSession || mActiveImmersiveSession) {
    194      promise->MaybeRejectWithInvalidStateError(
    195          "There can only be one immersive XRSession.");
    196      return promise.forget();
    197    }
    198    mPendingImmersiveSession = true;
    199  }
    200 
    201  bool isChromeSession = aCallerType == CallerType::System;
    202  uint32_t presentationGroup =
    203      isChromeSession ? gfx::kVRGroupChrome : gfx::kVRGroupContent;
    204  RefPtr<RequestSessionRequest> request = new RequestSessionRequest(
    205      aMode, presentationGroup, promise, requiredReferenceSpaceTypes,
    206      optionalReferenceSpaceTypes);
    207  if (request->WantsHardware()) {
    208    QueueSessionRequestWithEnumeration(request);
    209  } else {
    210    QueueSessionRequestWithoutEnumeration(request);
    211  }
    212 
    213  return promise.forget();
    214 }
    215 
    216 void XRSystem::QueueSessionRequestWithEnumeration(
    217    RequestSessionRequest* aRequest) {
    218  MOZ_ASSERT(aRequest->WantsHardware());
    219  mRequestSessionRequestsWaitingForRuntimeDetection.AppendElement(aRequest);
    220  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    221  vm->DetectRuntimes();
    222 }
    223 
    224 void XRSystem::QueueSessionRequestWithoutEnumeration(
    225    RequestSessionRequest* aRequest) {
    226  MOZ_ASSERT(!aRequest->NeedsHardware());
    227  mRequestSessionRequestsWithoutHardware.AppendElement(aRequest);
    228 
    229  ResolveSessionRequestsWithoutHardware();
    230 }
    231 
    232 bool XRSystem::CancelHardwareRequest(RequestSessionRequest* aRequest) {
    233  if (!aRequest->NeedsHardware()) {
    234    // If hardware access was an optional requirement and the user
    235    // opted not to provide access, queue the request
    236    // to be resolved without hardware.
    237    QueueSessionRequestWithoutEnumeration(aRequest);
    238    return false;
    239  }
    240 
    241  if (aRequest->IsImmersive()) {
    242    mPendingImmersiveSession = false;
    243  }
    244  return true;
    245 }
    246 
    247 bool XRSystem::OnXRPermissionRequestAllow() {
    248  if (!gfx::VRManagerChild::IsCreated()) {
    249    // It's possible that this callback returns after
    250    // we have already started shutting down.
    251    return false;
    252  }
    253  if (!mEnumerationInFlight) {
    254    mEnumerationInFlight = true;
    255    gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    256    (void)vm->EnumerateVRDisplays();
    257  }
    258  return mEnumerationInFlight ||
    259         !mRequestSessionRequestsWaitingForEnumeration.IsEmpty();
    260 }
    261 
    262 void XRSystem::OnXRPermissionRequestCancel() {
    263  nsTArray<RefPtr<RequestSessionRequest>> requestSessionRequests(
    264      std::move(mRequestSessionRequestsWaitingForEnumeration));
    265  for (RefPtr<RequestSessionRequest>& request : requestSessionRequests) {
    266    if (CancelHardwareRequest(request)) {
    267      request->mPromise->MaybeRejectWithSecurityError(
    268          "A device supporting the requested session "
    269          "configuration could not be found.");
    270    }
    271  }
    272 }
    273 
    274 bool XRSystem::FeaturePolicyBlocked() const {
    275  nsGlobalWindowInner* win = GetOwnerWindow();
    276  if (!win) {
    277    return true;
    278  }
    279  RefPtr<XRPermissionRequest> request =
    280      new XRPermissionRequest(win, win->WindowID());
    281  return !(request->CheckPermissionDelegate());
    282 }
    283 
    284 bool XRSystem::HasActiveImmersiveSession() const {
    285  return mActiveImmersiveSession;
    286 }
    287 
    288 void XRSystem::ResolveSessionRequestsWithoutHardware() {
    289  // Resolve promises returned by RequestSession
    290  nsTArray<RefPtr<gfx::VRDisplayClient>> displays;
    291  // Try resolving support without a device, for inline sessions.
    292  displays.AppendElement(nullptr);
    293 
    294  nsTArray<RefPtr<RequestSessionRequest>> requestSessionRequests(
    295      std::move(mRequestSessionRequestsWithoutHardware));
    296 
    297  ResolveSessionRequests(requestSessionRequests, displays);
    298 }
    299 
    300 void XRSystem::NotifyEnumerationCompleted() {
    301  // Enumeration has completed.
    302  mEnumerationInFlight = false;
    303 
    304  if (!gfx::VRManagerChild::IsCreated()) {
    305    // It's possible that this callback returns after
    306    // we have already started shutting down.
    307    return;
    308  }
    309 
    310  // Resolve promises returned by RequestSession
    311  nsTArray<RefPtr<gfx::VRDisplayClient>> displays;
    312  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    313  vm->GetVRDisplays(displays);
    314 
    315  nsTArray<RefPtr<RequestSessionRequest>> requestSessionRequests(
    316      std::move(mRequestSessionRequestsWaitingForEnumeration));
    317 
    318  ResolveSessionRequests(requestSessionRequests, displays);
    319 }
    320 
    321 void XRSystem::ResolveSessionRequests(
    322    nsTArray<RefPtr<RequestSessionRequest>>& aRequests,
    323    const nsTArray<RefPtr<gfx::VRDisplayClient>>& aDisplays) {
    324  for (RefPtr<RequestSessionRequest>& request : aRequests) {
    325    RefPtr<XRSession> session;
    326    if (request->IsImmersive()) {
    327      mPendingImmersiveSession = false;
    328    }
    329    // Select an XR device
    330    for (const RefPtr<gfx::VRDisplayClient>& display : aDisplays) {
    331      nsTArray<XRReferenceSpaceType> enabledReferenceSpaceTypes;
    332      if (request->ResolveSupport(display, enabledReferenceSpaceTypes)) {
    333        if (request->IsImmersive()) {
    334          session = XRSession::CreateImmersiveSession(
    335              GetOwnerWindow(), this, display, request->GetPresentationGroup(),
    336              enabledReferenceSpaceTypes);
    337          mActiveImmersiveSession = session;
    338        } else {
    339          session = XRSession::CreateInlineSession(GetOwnerWindow(), this,
    340                                                   enabledReferenceSpaceTypes);
    341          mInlineSessions.AppendElement(session);
    342        }
    343        request->mPromise->MaybeResolve(session);
    344        break;
    345      }
    346    }
    347    if (!session) {
    348      request->mPromise->MaybeRejectWithNotSupportedError(
    349          "A device supporting the required XRSession configuration "
    350          "could not be found.");
    351    }
    352  }
    353 }
    354 
    355 void XRSystem::NotifyDetectRuntimesCompleted() {
    356  ResolveIsSessionSupportedRequests();
    357  if (!mRequestSessionRequestsWaitingForRuntimeDetection.IsEmpty()) {
    358    ProcessSessionRequestsWaitingForRuntimeDetection();
    359  }
    360 }
    361 
    362 void XRSystem::ResolveIsSessionSupportedRequests() {
    363  // Resolve promises returned by IsSessionSupported
    364  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    365  nsTArray<RefPtr<IsSessionSupportedRequest>> isSessionSupportedRequests(
    366      std::move(mIsSessionSupportedRequests));
    367  bool featurePolicyBlocked = FeaturePolicyBlocked();
    368 
    369  for (RefPtr<IsSessionSupportedRequest>& request :
    370       isSessionSupportedRequests) {
    371    if (featurePolicyBlocked) {
    372      request->mPromise->MaybeRejectWithSecurityError(
    373          "The xr-spatial-tracking feature policy is required.");
    374      continue;
    375    }
    376 
    377    bool supported = false;
    378    switch (request->GetSessionMode()) {
    379      case XRSessionMode::Immersive_vr:
    380        supported = vm->RuntimeSupportsVR();
    381        break;
    382      case XRSessionMode::Immersive_ar:
    383        supported = vm->RuntimeSupportsAR();
    384        break;
    385      default:
    386        break;
    387    }
    388    request->mPromise->MaybeResolve(supported);
    389  }
    390 }
    391 
    392 void XRSystem::ProcessSessionRequestsWaitingForRuntimeDetection() {
    393  bool alreadyRequestedPermission =
    394      !mRequestSessionRequestsWaitingForEnumeration.IsEmpty();
    395  bool featurePolicyBlocked = FeaturePolicyBlocked();
    396  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    397 
    398  nsTArray<RefPtr<RequestSessionRequest>> sessionRequests(
    399      std::move(mRequestSessionRequestsWaitingForRuntimeDetection));
    400 
    401  for (RefPtr<RequestSessionRequest>& request : sessionRequests) {
    402    bool compatibleRuntime = false;
    403    switch (request->GetSessionMode()) {
    404      case XRSessionMode::Immersive_vr:
    405        compatibleRuntime = vm->RuntimeSupportsVR();
    406        break;
    407      case XRSessionMode::Immersive_ar:
    408        compatibleRuntime = vm->RuntimeSupportsAR();
    409        break;
    410      case XRSessionMode::Inline:
    411        compatibleRuntime = vm->RuntimeSupportsInline();
    412        break;
    413      default:
    414        break;
    415    }
    416    if (!compatibleRuntime) {
    417      // If none of the requested sessions are supported by a
    418      // runtime, early exit without showing a permission prompt.
    419      if (CancelHardwareRequest(request)) {
    420        request->mPromise->MaybeRejectWithNotSupportedError(
    421            "A device supporting the required XRSession configuration "
    422            "could not be found.");
    423      }
    424      continue;
    425    }
    426    if (featurePolicyBlocked) {
    427      // Don't show a permission prompt if blocked by feature policy.
    428      if (CancelHardwareRequest(request)) {
    429        request->mPromise->MaybeRejectWithSecurityError(
    430            "The xr-spatial-tracking feature policy is required.");
    431      }
    432      continue;
    433    }
    434    // To continue evaluating this request, it must wait for hardware
    435    // enumeration and permission request.
    436    mRequestSessionRequestsWaitingForEnumeration.AppendElement(request);
    437  }
    438 
    439  if (!mRequestSessionRequestsWaitingForEnumeration.IsEmpty() &&
    440      !alreadyRequestedPermission) {
    441    /**
    442     * Inline sessions will require only a user gesture
    443     * and should not trigger XR permission UI.
    444     * This is not a problem currently, as the only platforms
    445     * allowing xr-spatial-tracking for inline sessions do not
    446     * present a modal XR permission UI. (eg. Android Firefox Reality)
    447     */
    448    GetOwnerWindow()->RequestXRPermission();
    449  }
    450 }
    451 
    452 void XRSystem::NotifyVRDisplayMounted(uint32_t aDisplayID) {}
    453 void XRSystem::NotifyVRDisplayUnmounted(uint32_t aDisplayID) {}
    454 
    455 void XRSystem::NotifyVRDisplayConnect(uint32_t aDisplayID) {
    456  DispatchTrustedEvent(u"devicechange"_ns);
    457 }
    458 
    459 void XRSystem::NotifyVRDisplayDisconnect(uint32_t aDisplayID) {
    460  DispatchTrustedEvent(u"devicechange"_ns);
    461 }
    462 
    463 void XRSystem::NotifyVRDisplayPresentChange(uint32_t aDisplayID) {}
    464 void XRSystem::NotifyPresentationGenerationChanged(uint32_t aDisplayID) {
    465  if (mActiveImmersiveSession) {
    466    mActiveImmersiveSession->ExitPresent();
    467  }
    468 }
    469 bool XRSystem::GetStopActivityStatus() const { return true; }
    470 
    471 RequestSessionRequest::RequestSessionRequest(
    472    XRSessionMode aSessionMode, uint32_t aPresentationGroup, Promise* aPromise,
    473    const nsTArray<XRReferenceSpaceType>& aRequiredReferenceSpaceTypes,
    474    const nsTArray<XRReferenceSpaceType>& aOptionalReferenceSpaceTypes)
    475    : mPromise(aPromise),
    476      mSessionMode(aSessionMode),
    477      mPresentationGroup(aPresentationGroup),
    478      mRequiredReferenceSpaceTypes(aRequiredReferenceSpaceTypes.Clone()),
    479      mOptionalReferenceSpaceTypes(aOptionalReferenceSpaceTypes.Clone()) {}
    480 
    481 bool RequestSessionRequest::ResolveSupport(
    482    const gfx::VRDisplayClient* aDisplay,
    483    nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes) const {
    484  if (aDisplay) {
    485    if (!aDisplay->GetIsConnected()) {
    486      return false;
    487    }
    488    if ((aDisplay->GetDisplayInfo().GetPresentingGroups() &
    489         mPresentationGroup) != 0) {
    490      return false;
    491    }
    492 
    493    const gfx::VRDisplayInfo& info = aDisplay->GetDisplayInfo();
    494    switch (mSessionMode) {
    495      case XRSessionMode::Inline:
    496        if (!bool(info.mDisplayState.capabilityFlags &
    497                  gfx::VRDisplayCapabilityFlags::Cap_Inline)) {
    498          return false;
    499        }
    500        break;
    501      case XRSessionMode::Immersive_vr:
    502        if (!bool(info.mDisplayState.capabilityFlags &
    503                  gfx::VRDisplayCapabilityFlags::Cap_ImmersiveVR)) {
    504          return false;
    505        }
    506        break;
    507      case XRSessionMode::Immersive_ar:
    508        if (!bool(info.mDisplayState.capabilityFlags &
    509                  gfx::VRDisplayCapabilityFlags::Cap_ImmersiveAR)) {
    510          return false;
    511        }
    512        break;
    513      default:
    514        break;
    515    }
    516  } else if (mSessionMode != XRSessionMode::Inline) {
    517    // If we don't have a device, we can only support inline sessions
    518    return false;
    519  }
    520 
    521  // All sessions support XRReferenceSpaceType::Viewer by default
    522  aEnabledReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Viewer);
    523 
    524  // Immersive sessions support XRReferenceSpaceType::Local by default
    525  if (IsImmersive()) {
    526    aEnabledReferenceSpaceTypes.AppendElement(XRReferenceSpaceType::Local);
    527  }
    528 
    529  for (XRReferenceSpaceType type : mRequiredReferenceSpaceTypes) {
    530    if (aDisplay) {
    531      if (!aDisplay->IsReferenceSpaceTypeSupported(type)) {
    532        return false;
    533      }
    534    } else if (type != XRReferenceSpaceType::Viewer) {
    535      // If we don't have a device, We only support
    536      // XRReferenceSpaceType::Viewer
    537      return false;
    538    }
    539    if (!aEnabledReferenceSpaceTypes.Contains(type)) {
    540      aEnabledReferenceSpaceTypes.AppendElement(type);
    541    }
    542  }
    543  if (aDisplay) {
    544    for (XRReferenceSpaceType type : mOptionalReferenceSpaceTypes) {
    545      if (aDisplay->IsReferenceSpaceTypeSupported(type) &&
    546          !aEnabledReferenceSpaceTypes.Contains(type)) {
    547        aEnabledReferenceSpaceTypes.AppendElement(type);
    548      }
    549    }
    550  }
    551  return true;
    552 }
    553 
    554 bool RequestSessionRequest::IsImmersive() const {
    555  return (mSessionMode == XRSessionMode::Immersive_vr ||
    556          mSessionMode == XRSessionMode::Immersive_ar);
    557 }
    558 
    559 bool RequestSessionRequest::WantsHardware() const {
    560  for (XRReferenceSpaceType type : mOptionalReferenceSpaceTypes) {
    561    // Any XRReferenceSpaceType other than Viewer requires hardware
    562    if (type != XRReferenceSpaceType::Viewer) {
    563      return true;
    564    }
    565  }
    566  return NeedsHardware();
    567 }
    568 
    569 bool RequestSessionRequest::NeedsHardware() const {
    570  for (XRReferenceSpaceType type : mRequiredReferenceSpaceTypes) {
    571    // Any XRReferenceSpaceType other than Viewer requires hardware
    572    if (type != XRReferenceSpaceType::Viewer) {
    573      return true;
    574    }
    575  }
    576  return false;
    577 }
    578 
    579 XRSessionMode RequestSessionRequest::GetSessionMode() const {
    580  return mSessionMode;
    581 }
    582 
    583 uint32_t RequestSessionRequest::GetPresentationGroup() const {
    584  return mPresentationGroup;
    585 }
    586 
    587 ////////////////////////////////////////////////////////////////////////////////
    588 // IsSessionSupportedRequest cycle collection
    589 NS_IMPL_CYCLE_COLLECTION(IsSessionSupportedRequest, mPromise)
    590 
    591 XRSessionMode IsSessionSupportedRequest::GetSessionMode() const {
    592  return mSessionMode;
    593 }
    594 
    595 ////////////////////////////////////////////////////////////////////////////////
    596 // XRRequestSessionPermissionRequest cycle collection
    597 NS_IMPL_CYCLE_COLLECTION_INHERITED(XRRequestSessionPermissionRequest,
    598                                   ContentPermissionRequestBase)
    599 
    600 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(
    601    XRRequestSessionPermissionRequest, ContentPermissionRequestBase)
    602 
    603 XRRequestSessionPermissionRequest::XRRequestSessionPermissionRequest(
    604    nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
    605    AllowCallback&& aAllowCallback,
    606    AllowAnySiteCallback&& aAllowAnySiteCallback,
    607    CancelCallback&& aCancelCallback)
    608    : ContentPermissionRequestBase(aNodePrincipal, aWindow, "dom.xr"_ns,
    609                                   "xr"_ns),
    610      mAllowCallback(std::move(aAllowCallback)),
    611      mAllowAnySiteCallback(std::move(aAllowAnySiteCallback)),
    612      mCancelCallback(std::move(aCancelCallback)),
    613      mCallbackCalled(false) {
    614  mPermissionRequests.AppendElement(
    615      PermissionRequest(mType, nsTArray<nsString>()));
    616 }
    617 
    618 XRRequestSessionPermissionRequest::~XRRequestSessionPermissionRequest() {
    619  Cancel();
    620 }
    621 
    622 NS_IMETHODIMP
    623 XRRequestSessionPermissionRequest::Cancel() {
    624  if (!mCallbackCalled) {
    625    mCallbackCalled = true;
    626    mCancelCallback();
    627  }
    628  return NS_OK;
    629 }
    630 
    631 NS_IMETHODIMP
    632 XRRequestSessionPermissionRequest::Allow(JS::Handle<JS::Value> aChoices) {
    633  nsTArray<PermissionChoice> choices;
    634  nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
    635  if (NS_FAILED(rv)) {
    636    return rv;
    637  }
    638 
    639  // There is no support to allow grants automatically from the prompting code
    640  // path.
    641 
    642  if (!mCallbackCalled) {
    643    mCallbackCalled = true;
    644    if (choices.Length() == 1 &&
    645        choices[0].choice().EqualsLiteral("allow-on-any-site")) {
    646      mAllowAnySiteCallback();
    647    } else if (choices.Length() == 1 &&
    648               choices[0].choice().EqualsLiteral("allow")) {
    649      mAllowCallback();
    650    }
    651  }
    652  return NS_OK;
    653 }
    654 
    655 already_AddRefed<XRRequestSessionPermissionRequest>
    656 XRRequestSessionPermissionRequest::Create(
    657    nsPIDOMWindowInner* aWindow, AllowCallback&& aAllowCallback,
    658    AllowAnySiteCallback&& aAllowAnySiteCallback,
    659    CancelCallback&& aCancelCallback) {
    660  if (!aWindow) {
    661    return nullptr;
    662  }
    663  nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(aWindow);
    664  if (!win->GetPrincipal()) {
    665    return nullptr;
    666  }
    667  RefPtr<XRRequestSessionPermissionRequest> request =
    668      new XRRequestSessionPermissionRequest(
    669          aWindow, win->GetPrincipal(), std::move(aAllowCallback),
    670          std::move(aAllowAnySiteCallback), std::move(aCancelCallback));
    671  return request.forget();
    672 }
    673 
    674 ////////////////////////////////////////////////////////////////////////////////
    675 // RequestSessionRequest cycle collection
    676 NS_IMPL_CYCLE_COLLECTION(RequestSessionRequest, mPromise)
    677 
    678 }  // namespace mozilla::dom