tor-browser

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

XRSession.cpp (18562B)


      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/XRSession.h"
      8 
      9 #include "VRDisplayClient.h"
     10 #include "VRDisplayPresentation.h"
     11 #include "VRLayerChild.h"
     12 #include "XRBoundedReferenceSpace.h"
     13 #include "XRFrame.h"
     14 #include "XRInputSourceArray.h"
     15 #include "XRNativeOrigin.h"
     16 #include "XRNativeOriginFixed.h"
     17 #include "XRNativeOriginLocal.h"
     18 #include "XRNativeOriginLocalFloor.h"
     19 #include "XRNativeOriginViewer.h"
     20 #include "XRRenderState.h"
     21 #include "XRSystem.h"
     22 #include "XRView.h"
     23 #include "XRViewerPose.h"
     24 #include "mozilla/EventDispatcher.h"
     25 #include "mozilla/dom/DocumentInlines.h"
     26 #include "mozilla/dom/Promise.h"
     27 #include "mozilla/dom/XRInputSourceEvent.h"
     28 #include "mozilla/dom/XRSessionEvent.h"
     29 #include "nsGlobalWindowInner.h"
     30 #include "nsIObserverService.h"
     31 #include "nsISupportsPrimitives.h"
     32 #include "nsRefreshDriver.h"
     33 
     34 /**
     35 * Maximum instances of XRFrame and XRViewerPose objects
     36 * created in the pool.
     37 */
     38 const uint32_t kMaxPoolSize = 16;
     39 
     40 namespace mozilla::dom {
     41 
     42 NS_IMPL_CYCLE_COLLECTION_CLASS(XRSession)
     43 
     44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XRSession,
     45                                                  DOMEventTargetHelper)
     46  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXRSystem)
     47  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveRenderState)
     48  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingRenderState)
     49  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputSources)
     50  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mViewerPosePool)
     51  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFramePool)
     52 
     53  for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     54    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     55    cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
     56  }
     57 
     58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     59 
     60 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XRSession, DOMEventTargetHelper)
     61  NS_IMPL_CYCLE_COLLECTION_UNLINK(mXRSystem)
     62  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveRenderState)
     63  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingRenderState)
     64  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputSources)
     65  NS_IMPL_CYCLE_COLLECTION_UNLINK(mViewerPosePool)
     66  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFramePool)
     67 
     68  tmp->mFrameRequestCallbacks.Clear();
     69 
     70  // Don't need NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER because
     71  // DOMEventTargetHelper does it for us.
     72 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     73 
     74 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XRSession, DOMEventTargetHelper)
     75 
     76 already_AddRefed<XRSession> XRSession::CreateInlineSession(
     77    nsPIDOMWindowInner* aWindow, XRSystem* aXRSystem,
     78    const nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes) {
     79  nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(aWindow);
     80  if (!win) {
     81    return nullptr;
     82  }
     83  Document* doc = aWindow->GetExtantDoc();
     84  if (!doc) {
     85    return nullptr;
     86  }
     87  nsPresContext* context = doc->GetPresContext();
     88  if (!context) {
     89    return nullptr;
     90  }
     91  nsRefreshDriver* driver = context->RefreshDriver();
     92  if (!driver) {
     93    return nullptr;
     94  }
     95 
     96  RefPtr<XRSession> session =
     97      new XRSession(aWindow, aXRSystem, driver, nullptr, gfx::kVRGroupContent,
     98                    aEnabledReferenceSpaceTypes);
     99  driver->AddRefreshObserver(session, FlushType::Display, "XR Session");
    100  return session.forget();
    101 }
    102 
    103 already_AddRefed<XRSession> XRSession::CreateImmersiveSession(
    104    nsPIDOMWindowInner* aWindow, XRSystem* aXRSystem,
    105    gfx::VRDisplayClient* aClient, uint32_t aPresentationGroup,
    106    const nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes) {
    107  RefPtr<XRSession> session =
    108      new XRSession(aWindow, aXRSystem, nullptr, aClient, aPresentationGroup,
    109                    aEnabledReferenceSpaceTypes);
    110  return session.forget();
    111 }
    112 
    113 XRSession::XRSession(
    114    nsPIDOMWindowInner* aWindow, XRSystem* aXRSystem,
    115    nsRefreshDriver* aRefreshDriver, gfx::VRDisplayClient* aClient,
    116    uint32_t aPresentationGroup,
    117    const nsTArray<XRReferenceSpaceType>& aEnabledReferenceSpaceTypes)
    118    : DOMEventTargetHelper(aWindow),
    119      mXRSystem(aXRSystem),
    120      mShutdown(false),
    121      mEnded(false),
    122      mRefreshDriver(aRefreshDriver),
    123      mDisplayClient(aClient),
    124      mFrameRequestCallbackCounter(0),
    125      mEnabledReferenceSpaceTypes(aEnabledReferenceSpaceTypes.Clone()),
    126      mViewerPosePoolIndex(0),
    127      mFramePoolIndex(0) {
    128  if (aClient) {
    129    aClient->SessionStarted(this);
    130  }
    131  mActiveRenderState = new XRRenderState(aWindow, this);
    132  mStartTimeStamp = TimeStamp::Now();
    133  if (IsImmersive()) {
    134    mDisplayPresentation =
    135        mDisplayClient->BeginPresentation({}, aPresentationGroup);
    136  }
    137  if (mDisplayClient) {
    138    mDisplayClient->SetXRAPIMode(gfx::VRAPIMode::WebXR);
    139  }
    140  // TODO: Handle XR input sources are no longer available cases.
    141  // https://immersive-web.github.io/webxr/#dom-xrsession-inputsources
    142  mInputSources = new XRInputSourceArray(aWindow);
    143 }
    144 
    145 XRSession::~XRSession() { MOZ_ASSERT(mShutdown); }
    146 
    147 gfx::VRDisplayClient* XRSession::GetDisplayClient() const {
    148  return mDisplayClient;
    149 }
    150 
    151 JSObject* XRSession::WrapObject(JSContext* aCx,
    152                                JS::Handle<JSObject*> aGivenProto) {
    153  return XRSession_Binding::Wrap(aCx, this, aGivenProto);
    154 }
    155 
    156 bool XRSession::IsEnded() const { return mEnded; }
    157 
    158 already_AddRefed<Promise> XRSession::End(ErrorResult& aRv) {
    159  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    160  NS_ENSURE_TRUE(global, nullptr);
    161 
    162  ExitPresentInternal();
    163 
    164  RefPtr<Promise> promise = Promise::Create(global, aRv);
    165  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    166 
    167  promise->MaybeResolve(JS::UndefinedHandleValue);
    168 
    169  return promise.forget();
    170 }
    171 
    172 bool XRSession::IsImmersive() const {
    173  // Only immersive sessions have a VRDisplayClient
    174  return mDisplayClient != nullptr;
    175 }
    176 
    177 XRVisibilityState XRSession::VisibilityState() const {
    178  return XRVisibilityState::Visible;
    179  // TODO (Bug 1609771): Implement changing visibility state
    180 }
    181 
    182 // https://immersive-web.github.io/webxr/#poses-may-be-reported
    183 // Given that an XRSession cannot be requested without explicit consent
    184 // by the user, the only necessary check is whether the XRSession's
    185 // visiblityState is 'visible'.
    186 bool XRSession::CanReportPoses() const {
    187  return VisibilityState() == XRVisibilityState::Visible;
    188 }
    189 
    190 // https://immersive-web.github.io/webxr/#dom-xrsession-updaterenderstate
    191 void XRSession::UpdateRenderState(const XRRenderStateInit& aNewState,
    192                                  ErrorResult& aRv) {
    193  if (mEnded) {
    194    aRv.ThrowInvalidStateError(
    195        "UpdateRenderState can not be called on an XRSession that has ended.");
    196    return;
    197  }
    198  if (aNewState.mBaseLayer.WasPassed() &&
    199      aNewState.mBaseLayer.Value()->mSession != this) {
    200    aRv.ThrowInvalidStateError(
    201        "The baseLayer passed in to UpdateRenderState must "
    202        "belong to the XRSession that UpdateRenderState is "
    203        "being called on.");
    204    return;
    205  }
    206  if (aNewState.mInlineVerticalFieldOfView.WasPassed() && IsImmersive()) {
    207    aRv.ThrowInvalidStateError(
    208        "The inlineVerticalFieldOfView can not be set on an "
    209        "XRRenderState for an immersive XRSession.");
    210    return;
    211  }
    212  if (mPendingRenderState == nullptr) {
    213    mPendingRenderState = new XRRenderState(*mActiveRenderState);
    214  }
    215  if (aNewState.mDepthNear.WasPassed()) {
    216    mPendingRenderState->SetDepthNear(aNewState.mDepthNear.Value());
    217  }
    218  if (aNewState.mDepthFar.WasPassed()) {
    219    mPendingRenderState->SetDepthFar(aNewState.mDepthFar.Value());
    220  }
    221  if (aNewState.mInlineVerticalFieldOfView.WasPassed()) {
    222    mPendingRenderState->SetInlineVerticalFieldOfView(
    223        aNewState.mInlineVerticalFieldOfView.Value());
    224  }
    225  if (aNewState.mBaseLayer.WasPassed()) {
    226    mPendingRenderState->SetBaseLayer(aNewState.mBaseLayer.Value());
    227  }
    228 }
    229 
    230 XRRenderState* XRSession::RenderState() { return mActiveRenderState; }
    231 
    232 XRInputSourceArray* XRSession::InputSources() { return mInputSources; }
    233 
    234 Nullable<float> XRSession::GetFrameRate() { return {}; }
    235 
    236 void XRSession::GetSupportedFrameRates(JSContext*,
    237                                       JS::MutableHandle<JSObject*> aRetVal) {
    238  aRetVal.set(nullptr);
    239 }
    240 
    241 // https://immersive-web.github.io/webxr/#apply-the-pending-render-state
    242 void XRSession::ApplyPendingRenderState() {
    243  if (mPendingRenderState == nullptr) {
    244    return;
    245  }
    246  mActiveRenderState = mPendingRenderState;
    247  mPendingRenderState = nullptr;
    248 
    249  // https://immersive-web.github.io/webxr/#minimum-inline-field-of-view
    250  const double kMinimumInlineVerticalFieldOfView = 0.0f;
    251 
    252  // https://immersive-web.github.io/webxr/#maximum-inline-field-of-view
    253  const double kMaximumInlineVerticalFieldOfView = M_PI;
    254 
    255  if (!mActiveRenderState->GetInlineVerticalFieldOfView().IsNull()) {
    256    double verticalFOV =
    257        mActiveRenderState->GetInlineVerticalFieldOfView().Value();
    258    if (verticalFOV < kMinimumInlineVerticalFieldOfView) {
    259      verticalFOV = kMinimumInlineVerticalFieldOfView;
    260    }
    261    if (verticalFOV > kMaximumInlineVerticalFieldOfView) {
    262      verticalFOV = kMaximumInlineVerticalFieldOfView;
    263    }
    264    mActiveRenderState->SetInlineVerticalFieldOfView(verticalFOV);
    265  }
    266 
    267  // Our minimum near plane value is set to a small value close but not equal to
    268  // zero (kEpsilon) The maximum far plane is infinite.
    269  const float kEpsilon = 0.00001f;
    270  double depthNear = mActiveRenderState->DepthNear();
    271  double depthFar = mActiveRenderState->DepthFar();
    272  if (depthNear < 0.0f) {
    273    depthNear = 0.0f;
    274  }
    275  if (depthFar < 0.0f) {
    276    depthFar = 0.0f;
    277  }
    278  // Ensure at least a small distance between the near and far planes
    279  if (fabs(depthFar - depthNear) < kEpsilon) {
    280    depthFar = depthNear + kEpsilon;
    281  }
    282  mActiveRenderState->SetDepthNear(depthNear);
    283  mActiveRenderState->SetDepthFar(depthFar);
    284 
    285  XRWebGLLayer* baseLayer = mActiveRenderState->GetBaseLayer();
    286  if (baseLayer) {
    287    if (!IsImmersive() && baseLayer->mCompositionDisabled) {
    288      mActiveRenderState->SetCompositionDisabled(true);
    289      mActiveRenderState->SetOutputCanvas(baseLayer->GetCanvas());
    290    } else {
    291      mActiveRenderState->SetCompositionDisabled(false);
    292      mActiveRenderState->SetOutputCanvas(nullptr);
    293      mDisplayPresentation->UpdateXRWebGLLayer(baseLayer);
    294    }
    295  }  // if (baseLayer)
    296 }
    297 
    298 void XRSession::WillRefresh(mozilla::TimeStamp aTime) {
    299  // Inline sessions are driven by nsRefreshDriver directly,
    300  // unlike immersive sessions, which are driven VRDisplayClient.
    301  if (!IsImmersive() && !mXRSystem->HasActiveImmersiveSession()) {
    302    if (nsIGlobalObject* global = GetOwnerGlobal()) {
    303      if (JSObject* obj = global->GetGlobalJSObject()) {
    304        js::NotifyAnimationActivity(obj);
    305      }
    306    }
    307    StartFrame();
    308  }
    309 }
    310 
    311 void XRSession::StartFrame() {
    312  if (mShutdown || mEnded) {
    313    return;
    314  }
    315  ApplyPendingRenderState();
    316 
    317  XRWebGLLayer* baseLayer = mActiveRenderState->GetBaseLayer();
    318  if (!baseLayer) {
    319    return;
    320  }
    321 
    322  if (!IsImmersive() && mActiveRenderState->GetOutputCanvas() == nullptr) {
    323    return;
    324  }
    325 
    326  // Determine timestamp for the callbacks
    327  TimeStamp nowTime = TimeStamp::Now();
    328  mozilla::TimeDuration duration = nowTime - mStartTimeStamp;
    329  DOMHighResTimeStamp timeStamp = duration.ToMilliseconds();
    330 
    331  // Create an XRFrame for the callbacks
    332  RefPtr<XRFrame> frame = PooledFrame();
    333  frame->StartAnimationFrame();
    334 
    335  baseLayer->StartAnimationFrame();
    336  nsTArray<XRFrameRequest> callbacks;
    337  callbacks.AppendElements(mFrameRequestCallbacks);
    338  mFrameRequestCallbacks.Clear();
    339  for (auto& callback : callbacks) {
    340    callback.Call(timeStamp, *frame);
    341  }
    342 
    343  baseLayer->EndAnimationFrame();
    344  frame->EndAnimationFrame();
    345  if (mDisplayPresentation) {
    346    mDisplayPresentation->SubmitFrame();
    347  }
    348 }
    349 
    350 void XRSession::ExitPresent() { ExitPresentInternal(); }
    351 
    352 already_AddRefed<Promise> XRSession::RequestReferenceSpace(
    353    const XRReferenceSpaceType& aReferenceSpaceType, ErrorResult& aRv) {
    354  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    355  NS_ENSURE_TRUE(global, nullptr);
    356 
    357  RefPtr<Promise> promise = Promise::Create(global, aRv);
    358  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    359 
    360  if (!mEnabledReferenceSpaceTypes.Contains(aReferenceSpaceType)) {
    361    promise->MaybeRejectWithNotSupportedError(nsLiteralCString(
    362        "Requested XRReferenceSpaceType not available for the XRSession."));
    363    return promise.forget();
    364  }
    365  RefPtr<XRReferenceSpace> space;
    366  RefPtr<XRNativeOrigin> nativeOrigin;
    367  if (mDisplayClient) {
    368    switch (aReferenceSpaceType) {
    369      case XRReferenceSpaceType::Viewer:
    370        nativeOrigin = new XRNativeOriginViewer(mDisplayClient);
    371        break;
    372      case XRReferenceSpaceType::Local:
    373        nativeOrigin = new XRNativeOriginLocal(mDisplayClient);
    374        break;
    375      case XRReferenceSpaceType::Local_floor:
    376      case XRReferenceSpaceType::Bounded_floor:
    377        nativeOrigin = new XRNativeOriginLocalFloor(mDisplayClient);
    378        break;
    379      default:
    380        nativeOrigin = new XRNativeOriginFixed(gfx::PointDouble3D());
    381        break;
    382    }
    383  } else {
    384    // We currently only support XRReferenceSpaceType::Viewer when
    385    // there is no XR hardware.  In this case, the native origin
    386    // will always be at {0, 0, 0} which will always be the same
    387    // as the 'tracked' position of the non-existant pose.
    388    MOZ_ASSERT(aReferenceSpaceType == XRReferenceSpaceType::Viewer);
    389    nativeOrigin = new XRNativeOriginFixed(gfx::PointDouble3D());
    390  }
    391  if (aReferenceSpaceType == XRReferenceSpaceType::Bounded_floor) {
    392    space = new XRBoundedReferenceSpace(GetParentObject(), this, nativeOrigin);
    393  } else {
    394    space = new XRReferenceSpace(GetParentObject(), this, nativeOrigin,
    395                                 aReferenceSpaceType);
    396  }
    397 
    398  promise->MaybeResolve(space);
    399  return promise.forget();
    400 }
    401 
    402 already_AddRefed<Promise> XRSession::UpdateTargetFrameRate(float aRate,
    403                                                           ErrorResult& aRv) {
    404  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    405  NS_ENSURE_TRUE(global, nullptr);
    406 
    407  RefPtr<Promise> promise = Promise::Create(global, aRv);
    408  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    409 
    410  if (mEnded) {
    411    promise->MaybeRejectWithInvalidStateError(
    412        "UpdateTargetFrameRate can not be called on an XRSession that has "
    413        "ended.");
    414    return promise.forget();
    415  }
    416 
    417  // https://immersive-web.github.io/webxr/#dom-xrsession-updatetargetframerate
    418  // TODO: Validate the rate with the frame rates supported from the device.
    419  // We add a no op for now to avoid JS exceptions related to undefined method.
    420  // The spec states that user agent MAY use rate to calculate a new display
    421  // frame rate, so it's fine to let the default frame rate for now.
    422 
    423  promise->MaybeResolve(JS::UndefinedHandleValue);
    424  return promise.forget();
    425 }
    426 
    427 XRRenderState* XRSession::GetActiveRenderState() const {
    428  return mActiveRenderState;
    429 }
    430 
    431 void XRSession::XRFrameRequest::Call(const DOMHighResTimeStamp& aTimeStamp,
    432                                     XRFrame& aFrame) {
    433  RefPtr<mozilla::dom::XRFrameRequestCallback> callback = mCallback;
    434  callback->Call(aTimeStamp, aFrame);
    435 }
    436 
    437 int32_t XRSession::RequestAnimationFrame(XRFrameRequestCallback& aCallback,
    438                                         ErrorResult& aError) {
    439  if (mShutdown) {
    440    return 0;
    441  }
    442 
    443  int32_t handle = ++mFrameRequestCallbackCounter;
    444 
    445  mFrameRequestCallbacks.AppendElement(XRFrameRequest(aCallback, handle));
    446 
    447  return handle;
    448 }
    449 
    450 void XRSession::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) {
    451  mFrameRequestCallbacks.RemoveElementSorted(aHandle);
    452 }
    453 
    454 void XRSession::Shutdown() {
    455  mShutdown = true;
    456  ExitPresentInternal();
    457  mViewerPosePool.Clear();
    458  mViewerPosePoolIndex = 0;
    459  mFramePool.Clear();
    460  mFramePoolIndex = 0;
    461  mActiveRenderState = nullptr;
    462  mPendingRenderState = nullptr;
    463  mFrameRequestCallbacks.Clear();
    464 
    465  // Unregister from nsRefreshObserver
    466  if (mRefreshDriver) {
    467    mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display);
    468    mRefreshDriver = nullptr;
    469  }
    470 }
    471 
    472 void XRSession::ExitPresentInternal() {
    473  if (mInputSources) {
    474    mInputSources->Clear(this);
    475  }
    476  if (mDisplayClient) {
    477    mDisplayClient->SessionEnded(this);
    478  }
    479 
    480  if (mXRSystem) {
    481    mXRSystem->SessionEnded(this);
    482  }
    483 
    484  if (mActiveRenderState) {
    485    mActiveRenderState->SessionEnded();
    486  }
    487 
    488  if (mPendingRenderState) {
    489    mPendingRenderState->SessionEnded();
    490  }
    491 
    492  mDisplayPresentation = nullptr;
    493  if (!mEnded) {
    494    mEnded = true;
    495 
    496    XRSessionEventInit init;
    497    init.mBubbles = false;
    498    init.mCancelable = false;
    499    init.mSession = this;
    500    RefPtr<XRSessionEvent> event =
    501        XRSessionEvent::Constructor(this, u"end"_ns, init);
    502 
    503    event->SetTrusted(true);
    504    this->DispatchEvent(*event);
    505  }
    506 }
    507 
    508 void XRSession::DisconnectFromOwner() {
    509  MOZ_ASSERT(NS_IsMainThread());
    510  Shutdown();
    511  DOMEventTargetHelper::DisconnectFromOwner();
    512 }
    513 
    514 void XRSession::LastRelease() {
    515  // We don't want to wait for the GC to free up the presentation
    516  // for use in other documents, so we do this in LastRelease().
    517  Shutdown();
    518 }
    519 
    520 RefPtr<XRViewerPose> XRSession::PooledViewerPose(
    521    const gfx::Matrix4x4Double& aTransform, bool aEmulatedPosition) {
    522  RefPtr<XRViewerPose> pose;
    523  if (mViewerPosePool.Length() > mViewerPosePoolIndex) {
    524    pose = mViewerPosePool.ElementAt(mViewerPosePoolIndex);
    525    pose->Transform()->Update(aTransform);
    526    pose->SetEmulatedPosition(aEmulatedPosition);
    527  } else {
    528    RefPtr<XRRigidTransform> transform =
    529        new XRRigidTransform(static_cast<EventTarget*>(this), aTransform);
    530    nsTArray<RefPtr<XRView>> views;
    531    if (IsImmersive()) {
    532      views.AppendElement(new XRView(GetParentObject(), XREye::Left));
    533      views.AppendElement(new XRView(GetParentObject(), XREye::Right));
    534    } else {
    535      views.AppendElement(new XRView(GetParentObject(), XREye::None));
    536    }
    537    pose = new XRViewerPose(static_cast<EventTarget*>(this), transform,
    538                            aEmulatedPosition, views);
    539    mViewerPosePool.AppendElement(pose);
    540  }
    541 
    542  mViewerPosePoolIndex++;
    543  if (mViewerPosePoolIndex >= kMaxPoolSize) {
    544    mViewerPosePoolIndex = 0;
    545  }
    546 
    547  return pose;
    548 }
    549 
    550 RefPtr<XRFrame> XRSession::PooledFrame() {
    551  RefPtr<XRFrame> frame;
    552  if (mFramePool.Length() > mFramePoolIndex) {
    553    frame = mFramePool.ElementAt(mFramePoolIndex);
    554  } else {
    555    frame = new XRFrame(GetParentObject(), this);
    556    mFramePool.AppendElement(frame);
    557  }
    558 
    559  return frame;
    560 }
    561 
    562 }  // namespace mozilla::dom