tor-browser

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

VRDisplay.cpp (27745B)


      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/VRDisplay.h"
      8 
      9 #include "Navigator.h"
     10 #include "VRDisplayClient.h"
     11 #include "VRDisplayPresentation.h"
     12 #include "VRManagerChild.h"
     13 #include "gfxUtils.h"
     14 #include "gfxVR.h"
     15 #include "mozilla/Base64.h"
     16 #include "mozilla/HoldDropJSObjects.h"
     17 #include "mozilla/ProfilerMarkers.h"
     18 #include "mozilla/Services.h"
     19 #include "mozilla/StaticPrefs_dom.h"
     20 #include "mozilla/dom/Element.h"
     21 #include "mozilla/dom/ElementBinding.h"
     22 #include "mozilla/dom/Promise.h"
     23 #include "mozilla/dom/UserActivation.h"
     24 #include "mozilla/dom/VRDisplayBinding.h"
     25 #include "mozilla/gfx/DataSurfaceHelpers.h"
     26 #include "nsGlobalWindowInner.h"
     27 #include "nsIObserverService.h"
     28 #include "nsISupportsPrimitives.h"
     29 #include "nsWrapperCache.h"
     30 
     31 using namespace mozilla::gfx;
     32 
     33 namespace mozilla::dom {
     34 
     35 VRFieldOfView::VRFieldOfView(nsISupports* aParent, double aUpDegrees,
     36                             double aRightDegrees, double aDownDegrees,
     37                             double aLeftDegrees)
     38    : mParent(aParent),
     39      mUpDegrees(aUpDegrees),
     40      mRightDegrees(aRightDegrees),
     41      mDownDegrees(aDownDegrees),
     42      mLeftDegrees(aLeftDegrees) {}
     43 
     44 VRFieldOfView::VRFieldOfView(nsISupports* aParent,
     45                             const gfx::VRFieldOfView& aSrc)
     46    : mParent(aParent),
     47      mUpDegrees(aSrc.upDegrees),
     48      mRightDegrees(aSrc.rightDegrees),
     49      mDownDegrees(aSrc.downDegrees),
     50      mLeftDegrees(aSrc.leftDegrees) {}
     51 
     52 bool VRDisplayCapabilities::HasPosition() const {
     53  return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
     54         bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
     55 }
     56 
     57 bool VRDisplayCapabilities::HasOrientation() const {
     58  return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
     59 }
     60 
     61 bool VRDisplayCapabilities::HasExternalDisplay() const {
     62  return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_External);
     63 }
     64 
     65 bool VRDisplayCapabilities::CanPresent() const {
     66  return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Present);
     67 }
     68 
     69 uint32_t VRDisplayCapabilities::MaxLayers() const {
     70  return CanPresent() ? 1 : 0;
     71 }
     72 
     73 void VRDisplay::UpdateDisplayClient(
     74    already_AddRefed<gfx::VRDisplayClient> aClient) {
     75  mClient = std::move(aClient);
     76 }
     77 
     78 /*static*/
     79 bool VRDisplay::RefreshVRDisplays(uint64_t aWindowId) {
     80  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
     81  return vm && vm->RefreshVRDisplaysWithCallback(aWindowId);
     82 }
     83 
     84 /*static*/
     85 void VRDisplay::UpdateVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays,
     86                                 nsPIDOMWindowInner* aWindow) {
     87  nsTArray<RefPtr<VRDisplay>> displays;
     88 
     89  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
     90  nsTArray<RefPtr<gfx::VRDisplayClient>> updatedDisplays;
     91  if (vm) {
     92    vm->GetVRDisplays(updatedDisplays);
     93    for (size_t i = 0; i < updatedDisplays.Length(); i++) {
     94      RefPtr<gfx::VRDisplayClient> display = updatedDisplays[i];
     95      bool isNewDisplay = true;
     96      for (size_t j = 0; j < aDisplays.Length(); j++) {
     97        if (aDisplays[j]->GetClient()->GetDisplayInfo().GetDisplayID() ==
     98            display->GetDisplayInfo().GetDisplayID()) {
     99          displays.AppendElement(aDisplays[j]);
    100          isNewDisplay = false;
    101        } else {
    102          RefPtr<gfx::VRDisplayClient> ref = display;
    103          aDisplays[j]->UpdateDisplayClient(do_AddRef(display));
    104          displays.AppendElement(aDisplays[j]);
    105          isNewDisplay = false;
    106        }
    107      }
    108 
    109      if (isNewDisplay) {
    110        displays.AppendElement(new VRDisplay(aWindow, display));
    111      }
    112    }
    113  }
    114 
    115  aDisplays = std::move(displays);
    116 }
    117 
    118 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent)
    119 
    120 JSObject* VRFieldOfView::WrapObject(JSContext* aCx,
    121                                    JS::Handle<JSObject*> aGivenProto) {
    122  return VRFieldOfView_Binding::Wrap(aCx, this, aGivenProto);
    123 }
    124 
    125 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(VREyeParameters,
    126                                                      (mParent, mFOV),
    127                                                      (mOffset))
    128 
    129 VREyeParameters::VREyeParameters(nsISupports* aParent,
    130                                 const gfx::Point3D& aEyeTranslation,
    131                                 const gfx::VRFieldOfView& aFOV,
    132                                 const gfx::IntSize& aRenderSize)
    133    : mParent(aParent),
    134      mEyeTranslation(aEyeTranslation),
    135      mRenderSize(aRenderSize) {
    136  mFOV = new VRFieldOfView(aParent, aFOV);
    137  mozilla::HoldJSObjects(this);
    138 }
    139 
    140 VREyeParameters::~VREyeParameters() { mozilla::DropJSObjects(this); }
    141 
    142 VRFieldOfView* VREyeParameters::FieldOfView() { return mFOV; }
    143 
    144 void VREyeParameters::GetOffset(JSContext* aCx,
    145                                JS::MutableHandle<JSObject*> aRetval,
    146                                ErrorResult& aRv) {
    147  if (!mOffset) {
    148    // Lazily create the Float32Array
    149    mOffset =
    150        dom::Float32Array::Create(aCx, this, mEyeTranslation.components, aRv);
    151    if (aRv.Failed()) {
    152      return;
    153    }
    154  }
    155  aRetval.set(mOffset);
    156 }
    157 
    158 JSObject* VREyeParameters::WrapObject(JSContext* aCx,
    159                                      JS::Handle<JSObject*> aGivenProto) {
    160  return VREyeParameters_Binding::Wrap(aCx, this, aGivenProto);
    161 }
    162 
    163 VRStageParameters::VRStageParameters(
    164    nsISupports* aParent, const gfx::Matrix4x4& aSittingToStandingTransform,
    165    const gfx::Size& aSize)
    166    : mParent(aParent),
    167      mSittingToStandingTransform(aSittingToStandingTransform),
    168      mSittingToStandingTransformArray(nullptr),
    169      mSize(aSize) {
    170  mozilla::HoldJSObjects(this);
    171 }
    172 
    173 VRStageParameters::~VRStageParameters() { mozilla::DropJSObjects(this); }
    174 
    175 JSObject* VRStageParameters::WrapObject(JSContext* aCx,
    176                                        JS::Handle<JSObject*> aGivenProto) {
    177  return VRStageParameters_Binding::Wrap(aCx, this, aGivenProto);
    178 }
    179 
    180 NS_IMPL_CYCLE_COLLECTION_CLASS(VRStageParameters)
    181 
    182 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRStageParameters)
    183  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
    184  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    185  tmp->mSittingToStandingTransformArray = nullptr;
    186 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    187 
    188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRStageParameters)
    189  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
    190 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    191 
    192 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRStageParameters)
    193  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    194  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(
    195      mSittingToStandingTransformArray)
    196 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    197 
    198 void VRStageParameters::GetSittingToStandingTransform(
    199    JSContext* aCx, JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) {
    200  if (!mSittingToStandingTransformArray) {
    201    // Lazily create the Float32Array
    202    mSittingToStandingTransformArray = dom::Float32Array::Create(
    203        aCx, this, mSittingToStandingTransform.components, aRv);
    204    if (aRv.Failed()) {
    205      return;
    206    }
    207  }
    208  aRetval.set(mSittingToStandingTransformArray);
    209 }
    210 
    211 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDisplayCapabilities, mParent)
    212 
    213 JSObject* VRDisplayCapabilities::WrapObject(JSContext* aCx,
    214                                            JS::Handle<JSObject*> aGivenProto) {
    215  return VRDisplayCapabilities_Binding::Wrap(aCx, this, aGivenProto);
    216 }
    217 
    218 VRPose::VRPose(nsISupports* aParent, const gfx::VRHMDSensorState& aState)
    219    : Pose(aParent), mVRState(aState) {
    220  mozilla::HoldJSObjects(this);
    221 }
    222 
    223 VRPose::VRPose(nsISupports* aParent) : Pose(aParent) {
    224  mVRState.inputFrameID = 0;
    225  mVRState.timestamp = 0.0;
    226  mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None;
    227  mozilla::HoldJSObjects(this);
    228 }
    229 
    230 VRPose::~VRPose() { mozilla::DropJSObjects(this); }
    231 
    232 void VRPose::GetPosition(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval,
    233                         ErrorResult& aRv) {
    234  const bool valid =
    235      bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
    236      bool(mVRState.flags &
    237           gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
    238  SetFloat32Array(aCx, this, aRetval, mPosition,
    239                  valid ? mVRState.pose.position.data() : nullptr, 3, aRv);
    240 }
    241 
    242 void VRPose::GetLinearVelocity(JSContext* aCx,
    243                               JS::MutableHandle<JSObject*> aRetval,
    244                               ErrorResult& aRv) {
    245  const bool valid =
    246      bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) ||
    247      bool(mVRState.flags &
    248           gfx::VRDisplayCapabilityFlags::Cap_PositionEmulated);
    249  SetFloat32Array(aCx, this, aRetval, mLinearVelocity,
    250                  valid ? mVRState.pose.linearVelocity.data() : nullptr, 3,
    251                  aRv);
    252 }
    253 
    254 void VRPose::GetLinearAcceleration(JSContext* aCx,
    255                                   JS::MutableHandle<JSObject*> aRetval,
    256                                   ErrorResult& aRv) {
    257  const bool valid = bool(
    258      mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration);
    259  SetFloat32Array(aCx, this, aRetval, mLinearAcceleration,
    260                  valid ? mVRState.pose.linearAcceleration.data() : nullptr, 3,
    261                  aRv);
    262 }
    263 
    264 void VRPose::GetOrientation(JSContext* aCx,
    265                            JS::MutableHandle<JSObject*> aRetval,
    266                            ErrorResult& aRv) {
    267  const bool valid =
    268      bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
    269  SetFloat32Array(aCx, this, aRetval, mOrientation,
    270                  valid ? mVRState.pose.orientation.data() : nullptr, 4, aRv);
    271 }
    272 
    273 void VRPose::GetAngularVelocity(JSContext* aCx,
    274                                JS::MutableHandle<JSObject*> aRetval,
    275                                ErrorResult& aRv) {
    276  const bool valid =
    277      bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation);
    278  SetFloat32Array(aCx, this, aRetval, mAngularVelocity,
    279                  valid ? mVRState.pose.angularVelocity.data() : nullptr, 3,
    280                  aRv);
    281 }
    282 
    283 void VRPose::GetAngularAcceleration(JSContext* aCx,
    284                                    JS::MutableHandle<JSObject*> aRetval,
    285                                    ErrorResult& aRv) {
    286  const bool valid = bool(
    287      mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration);
    288  SetFloat32Array(aCx, this, aRetval, mAngularAcceleration,
    289                  valid ? mVRState.pose.angularAcceleration.data() : nullptr, 3,
    290                  aRv);
    291 }
    292 
    293 void VRPose::Update(const gfx::VRHMDSensorState& aState) { mVRState = aState; }
    294 
    295 JSObject* VRPose::WrapObject(JSContext* aCx,
    296                             JS::Handle<JSObject*> aGivenProto) {
    297  return VRPose_Binding::Wrap(aCx, this, aGivenProto);
    298 }
    299 
    300 /* virtual */
    301 JSObject* VRDisplay::WrapObject(JSContext* aCx,
    302                                JS::Handle<JSObject*> aGivenProto) {
    303  return VRDisplay_Binding::Wrap(aCx, this, aGivenProto);
    304 }
    305 
    306 VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient)
    307    : DOMEventTargetHelper(aWindow),
    308      mClient(aClient),
    309      mDepthNear(0.01f)  // Default value from WebVR Spec
    310      ,
    311      mDepthFar(10000.0f)  // Default value from WebVR Spec
    312      ,
    313      mVRNavigationEventDepth(0),
    314      mShutdown(false) {
    315  const gfx::VRDisplayInfo& info = aClient->GetDisplayInfo();
    316  mCapabilities = new VRDisplayCapabilities(aWindow, info.GetCapabilities());
    317  if (info.GetCapabilities() &
    318      gfx::VRDisplayCapabilityFlags::Cap_StageParameters) {
    319    mStageParameters = new VRStageParameters(
    320        aWindow, info.GetSittingToStandingTransform(), info.GetStageSize());
    321  }
    322  mozilla::HoldJSObjects(this);
    323  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    324  if (MOZ_LIKELY(obs)) {
    325    obs->AddObserver(this, "inner-window-destroyed", false);
    326  }
    327 }
    328 
    329 VRDisplay::~VRDisplay() {
    330  MOZ_ASSERT(mShutdown);
    331  mozilla::DropJSObjects(this);
    332 }
    333 
    334 void VRDisplay::LastRelease() {
    335  // We don't want to wait for the CC to free up the presentation
    336  // for use in other documents, so we do this in LastRelease().
    337  Shutdown();
    338 }
    339 
    340 already_AddRefed<VREyeParameters> VRDisplay::GetEyeParameters(VREye aEye) {
    341  gfx::VRDisplayState::Eye eye = aEye == VREye::Left
    342                                     ? gfx::VRDisplayState::Eye_Left
    343                                     : gfx::VRDisplayState::Eye_Right;
    344  RefPtr<VREyeParameters> params = new VREyeParameters(
    345      GetParentObject(), mClient->GetDisplayInfo().GetEyeTranslation(eye),
    346      mClient->GetDisplayInfo().GetEyeFOV(eye),
    347      mClient->GetDisplayInfo().SuggestedEyeResolution());
    348  return params.forget();
    349 }
    350 
    351 VRDisplayCapabilities* VRDisplay::Capabilities() { return mCapabilities; }
    352 
    353 VRStageParameters* VRDisplay::GetStageParameters() { return mStageParameters; }
    354 
    355 uint32_t VRDisplay::DisplayId() const {
    356  const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
    357  return info.GetDisplayID();
    358 }
    359 
    360 void VRDisplay::GetDisplayName(nsAString& aDisplayName) const {
    361  const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
    362  CopyUTF8toUTF16(MakeStringSpan(info.GetDisplayName()), aDisplayName);
    363 }
    364 
    365 void VRDisplay::UpdateFrameInfo() {
    366  /**
    367   * The WebVR 1.1 spec Requires that VRDisplay.getPose and
    368   * VRDisplay.getFrameData must return the same values until the next
    369   * VRDisplay.submitFrame.
    370   *
    371   * mFrameInfo is marked dirty at the end of the frame or start of a new
    372   * composition and lazily created here in order to receive mid-frame
    373   * pose-prediction updates while still ensuring conformance to the WebVR spec
    374   * requirements.
    375   *
    376   * If we are not presenting WebVR content, the frame will never end and we
    377   * should return the latest frame data always.
    378   */
    379  mFrameInfo.Clear();
    380 
    381  if ((mFrameInfo.IsDirty() && IsPresenting()) ||
    382      mClient->GetDisplayInfo().GetPresentingGroups() == 0) {
    383    const gfx::VRHMDSensorState& state = mClient->GetSensorState();
    384    const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo();
    385    mFrameInfo.Update(info, state, mDepthNear, mDepthFar);
    386  }
    387 }
    388 
    389 bool VRDisplay::GetFrameData(VRFrameData& aFrameData) {
    390  UpdateFrameInfo();
    391  if (!(mFrameInfo.mVRState.flags &
    392        gfx::VRDisplayCapabilityFlags::Cap_Orientation)) {
    393    // We must have at minimum Cap_Orientation for a valid pose.
    394    return false;
    395  }
    396  aFrameData.Update(mFrameInfo);
    397  return true;
    398 }
    399 
    400 already_AddRefed<VRPose> VRDisplay::GetPose() {
    401  UpdateFrameInfo();
    402  RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState);
    403 
    404  return obj.forget();
    405 }
    406 
    407 void VRDisplay::ResetPose() {
    408  // ResetPose is deprecated and unimplemented
    409  // We must keep this stub function around as its referenced by
    410  // VRDisplay.webidl. Not asserting here, as that could break existing web
    411  // content.
    412 }
    413 
    414 void VRDisplay::StartVRNavigation() { mClient->StartVRNavigation(); }
    415 
    416 void VRDisplay::StartHandlingVRNavigationEvent() {
    417  mHandlingVRNavigationEventStart = TimeStamp::Now();
    418  ++mVRNavigationEventDepth;
    419  TimeDuration timeout =
    420      TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
    421  // A 0 or negative TimeDuration indicates that content may take
    422  // as long as it wishes to respond to the event, as long as
    423  // it happens before the event exits.
    424  if (timeout.ToMilliseconds() > 0) {
    425    mClient->StopVRNavigation(timeout);
    426  }
    427 }
    428 
    429 void VRDisplay::StopHandlingVRNavigationEvent() {
    430  MOZ_ASSERT(mVRNavigationEventDepth > 0);
    431  --mVRNavigationEventDepth;
    432  if (mVRNavigationEventDepth == 0) {
    433    mClient->StopVRNavigation(TimeDuration::FromMilliseconds(0));
    434  }
    435 }
    436 
    437 bool VRDisplay::IsHandlingVRNavigationEvent() {
    438  if (mVRNavigationEventDepth == 0) {
    439    return false;
    440  }
    441  if (mHandlingVRNavigationEventStart.IsNull()) {
    442    return false;
    443  }
    444  TimeDuration timeout =
    445      TimeDuration::FromMilliseconds(StaticPrefs::dom_vr_navigation_timeout());
    446  return timeout.ToMilliseconds() <= 0 ||
    447         (TimeStamp::Now() - mHandlingVRNavigationEventStart) <= timeout;
    448 }
    449 
    450 void VRDisplay::OnPresentationGenerationChanged() { ExitPresentInternal(); }
    451 
    452 already_AddRefed<Promise> VRDisplay::RequestPresent(
    453    const nsTArray<VRLayer>& aLayers, CallerType aCallerType,
    454    ErrorResult& aRv) {
    455  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    456  if (!global) {
    457    aRv.Throw(NS_ERROR_FAILURE);
    458    return nullptr;
    459  }
    460 
    461  RefPtr<Promise> promise = Promise::Create(global, aRv);
    462  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    463 
    464  bool isChromePresentation = aCallerType == CallerType::System;
    465  uint32_t presentationGroup =
    466      isChromePresentation ? gfx::kVRGroupChrome : gfx::kVRGroupContent;
    467 
    468  mClient->SetXRAPIMode(gfx::VRAPIMode::WebVR);
    469  if (!UserActivation::IsHandlingUserInput() && !isChromePresentation &&
    470      !IsHandlingVRNavigationEvent() && StaticPrefs::dom_vr_require_gesture() &&
    471      !IsPresenting()) {
    472    // The WebVR API states that if called outside of a user gesture, the
    473    // promise must be rejected.  We allow VR presentations to start within
    474    // trusted events such as vrdisplayactivate, which triggers in response to
    475    // HMD proximity sensors and when navigating within a VR presentation.
    476    // This user gesture requirement is not enforced for chrome/system code.
    477    promise->MaybeRejectWithUndefined();
    478  } else if (!IsPresenting() && IsAnyPresenting(presentationGroup)) {
    479    // Only one presentation allowed per VRDisplay on a
    480    // first-come-first-serve basis.
    481    // If this Javascript context is presenting, then we can replace our
    482    // presentation with a new one containing new layers but we should never
    483    // replace the presentation of another context.
    484    // Simultaneous presentations in other groups are allowed in separate
    485    // Javascript contexts to enable browser UI from chrome/system contexts.
    486    // Eventually, this restriction will be loosened to enable multitasking
    487    // use cases.
    488    promise->MaybeRejectWithUndefined();
    489  } else {
    490    if (mPresentation) {
    491      mPresentation->UpdateLayers(aLayers);
    492    } else {
    493      mPresentation = mClient->BeginPresentation(aLayers, presentationGroup);
    494    }
    495    mFrameInfo.Clear();
    496    promise->MaybeResolve(JS::UndefinedHandleValue);
    497  }
    498  return promise.forget();
    499 }
    500 
    501 NS_IMETHODIMP
    502 VRDisplay::Observe(nsISupports* aSubject, const char* aTopic,
    503                   const char16_t* aData) {
    504  MOZ_ASSERT(NS_IsMainThread());
    505 
    506  if (strcmp(aTopic, "inner-window-destroyed") == 0) {
    507    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
    508    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
    509 
    510    uint64_t innerID;
    511    nsresult rv = wrapper->GetData(&innerID);
    512    NS_ENSURE_SUCCESS(rv, rv);
    513 
    514    if (!GetOwnerWindow() || GetOwnerWindow()->WindowID() == innerID) {
    515      Shutdown();
    516    }
    517 
    518    return NS_OK;
    519  }
    520 
    521  // This should not happen.
    522  return NS_ERROR_FAILURE;
    523 }
    524 
    525 already_AddRefed<Promise> VRDisplay::ExitPresent(ErrorResult& aRv) {
    526  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    527  if (!global) {
    528    aRv.Throw(NS_ERROR_FAILURE);
    529    return nullptr;
    530  }
    531 
    532  RefPtr<Promise> promise = Promise::Create(global, aRv);
    533  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
    534 
    535  if (!IsPresenting()) {
    536    // We can not exit a presentation outside of the context that
    537    // started the presentation.
    538    promise->MaybeRejectWithUndefined();
    539  } else {
    540    promise->MaybeResolve(JS::UndefinedHandleValue);
    541    ExitPresentInternal();
    542  }
    543 
    544  return promise.forget();
    545 }
    546 
    547 void VRDisplay::ExitPresentInternal() { mPresentation = nullptr; }
    548 
    549 void VRDisplay::Shutdown() {
    550  mShutdown = true;
    551  ExitPresentInternal();
    552  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    553  if (MOZ_LIKELY(obs)) {
    554    obs->RemoveObserver(this, "inner-window-destroyed");
    555  }
    556 }
    557 
    558 void VRDisplay::GetLayers(nsTArray<VRLayer>& result) {
    559  if (mPresentation) {
    560    mPresentation->GetDOMLayers(result);
    561  } else {
    562    result = nsTArray<VRLayer>();
    563  }
    564 }
    565 
    566 void VRDisplay::SubmitFrame() {
    567  AUTO_PROFILER_MARKER("SubmitFrameAtVRDisplay", OTHER);
    568 
    569  if (mClient && !mClient->IsPresentationGenerationCurrent()) {
    570    mPresentation = nullptr;
    571    mClient->MakePresentationGenerationCurrent();
    572  }
    573 
    574  if (mPresentation) {
    575    mPresentation->SubmitFrame();
    576  }
    577  mFrameInfo.Clear();
    578 }
    579 
    580 int32_t VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
    581                                         ErrorResult& aError) {
    582  if (mShutdown) {
    583    return 0;
    584  }
    585 
    586  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    587 
    588  int32_t handle;
    589  aError = vm->ScheduleFrameRequestCallback(aCallback, &handle);
    590  return handle;
    591 }
    592 
    593 void VRDisplay::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) {
    594  gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
    595  vm->CancelFrameRequestCallback(aHandle);
    596 }
    597 
    598 bool VRDisplay::IsPresenting() const {
    599  // IsPresenting returns true only if this Javascript context is presenting
    600  // and will return false if another context is presenting.
    601  return mPresentation != nullptr;
    602 }
    603 
    604 bool VRDisplay::IsAnyPresenting(uint32_t aGroupMask) const {
    605  // IsAnyPresenting returns true if either this VRDisplay object or any other
    606  // from anther Javascript context is presenting with a group matching
    607  // aGroupMask.
    608  if (mPresentation && (mPresentation->GetGroup() & aGroupMask)) {
    609    return true;
    610  }
    611  if (mClient->GetDisplayInfo().GetPresentingGroups() & aGroupMask) {
    612    return true;
    613  }
    614  return false;
    615 }
    616 
    617 bool VRDisplay::IsConnected() const { return mClient->GetIsConnected(); }
    618 
    619 uint32_t VRDisplay::PresentingGroups() const {
    620  return mClient->GetDisplayInfo().GetPresentingGroups();
    621 }
    622 
    623 uint32_t VRDisplay::GroupMask() const {
    624  return mClient->GetDisplayInfo().GetGroupMask();
    625 }
    626 
    627 void VRDisplay::SetGroupMask(const uint32_t& aGroupMask) {
    628  mClient->SetGroupMask(aGroupMask);
    629 }
    630 
    631 NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper,
    632                                   mCapabilities, mStageParameters)
    633 
    634 NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper)
    635 NS_IMPL_RELEASE_INHERITED(VRDisplay, DOMEventTargetHelper)
    636 
    637 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(VRDisplay)
    638  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    639  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, EventTarget)
    640 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    641 
    642 NS_IMPL_CYCLE_COLLECTION_CLASS(VRFrameData)
    643 
    644 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRFrameData)
    645  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPose)
    646  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    647  tmp->mLeftProjectionMatrix = nullptr;
    648  tmp->mLeftViewMatrix = nullptr;
    649  tmp->mRightProjectionMatrix = nullptr;
    650  tmp->mRightViewMatrix = nullptr;
    651 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    652 
    653 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRFrameData)
    654  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPose)
    655 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    656 
    657 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRFrameData)
    658  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    659  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftProjectionMatrix)
    660  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftViewMatrix)
    661  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightProjectionMatrix)
    662  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightViewMatrix)
    663 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    664 
    665 VRFrameData::VRFrameData(nsISupports* aParent)
    666    : mParent(aParent),
    667      mLeftProjectionMatrix(nullptr),
    668      mLeftViewMatrix(nullptr),
    669      mRightProjectionMatrix(nullptr),
    670      mRightViewMatrix(nullptr) {
    671  mozilla::HoldJSObjects(this);
    672  mPose = new VRPose(aParent);
    673 }
    674 
    675 VRFrameData::~VRFrameData() { mozilla::DropJSObjects(this); }
    676 
    677 /* static */
    678 already_AddRefed<VRFrameData> VRFrameData::Constructor(
    679    const GlobalObject& aGlobal) {
    680  RefPtr<VRFrameData> obj = new VRFrameData(aGlobal.GetAsSupports());
    681  return obj.forget();
    682 }
    683 
    684 JSObject* VRFrameData::WrapObject(JSContext* aCx,
    685                                  JS::Handle<JSObject*> aGivenProto) {
    686  return VRFrameData_Binding::Wrap(aCx, this, aGivenProto);
    687 }
    688 
    689 VRPose* VRFrameData::Pose() { return mPose; }
    690 
    691 double VRFrameData::Timestamp() const {
    692  // Converting from seconds to milliseconds
    693  return mFrameInfo.mVRState.timestamp * 1000.0f;
    694 }
    695 
    696 void VRFrameData::GetLeftProjectionMatrix(JSContext* aCx,
    697                                          JS::MutableHandle<JSObject*> aRetval,
    698                                          ErrorResult& aRv) {
    699  Pose::SetFloat32Array(aCx, this, aRetval, mLeftProjectionMatrix,
    700                        mFrameInfo.mLeftProjection.components, 16, aRv);
    701 }
    702 
    703 void VRFrameData::GetLeftViewMatrix(JSContext* aCx,
    704                                    JS::MutableHandle<JSObject*> aRetval,
    705                                    ErrorResult& aRv) {
    706  Pose::SetFloat32Array(aCx, this, aRetval, mLeftViewMatrix,
    707                        mFrameInfo.mLeftView.components, 16, aRv);
    708 }
    709 
    710 void VRFrameData::GetRightProjectionMatrix(JSContext* aCx,
    711                                           JS::MutableHandle<JSObject*> aRetval,
    712                                           ErrorResult& aRv) {
    713  Pose::SetFloat32Array(aCx, this, aRetval, mRightProjectionMatrix,
    714                        mFrameInfo.mRightProjection.components, 16, aRv);
    715 }
    716 
    717 void VRFrameData::GetRightViewMatrix(JSContext* aCx,
    718                                     JS::MutableHandle<JSObject*> aRetval,
    719                                     ErrorResult& aRv) {
    720  Pose::SetFloat32Array(aCx, this, aRetval, mRightViewMatrix,
    721                        mFrameInfo.mRightView.components, 16, aRv);
    722 }
    723 
    724 void VRFrameData::Update(const VRFrameInfo& aFrameInfo) {
    725  mFrameInfo = aFrameInfo;
    726  mPose->Update(mFrameInfo.mVRState);
    727 }
    728 
    729 void VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo,
    730                         const gfx::VRHMDSensorState& aState, float aDepthNear,
    731                         float aDepthFar) {
    732  mVRState = aState;
    733  if (mTimeStampOffset == 0.0f) {
    734    /**
    735     * A mTimeStampOffset value of 0.0f indicates that this is the first
    736     * iteration and an offset has not yet been set.
    737     *
    738     * Generate a value for mTimeStampOffset such that if aState.timestamp is
    739     * monotonically increasing, aState.timestamp + mTimeStampOffset will never
    740     * be a negative number and will start at a pseudo-random offset
    741     * between 1000.0f and 11000.0f seconds.
    742     *
    743     * We use a pseudo random offset rather than 0.0f just to discourage users
    744     * from making the assumption that the timestamp returned in the WebVR API
    745     * has a base of 0, which is not necessarily true in all UA's.
    746     */
    747    mTimeStampOffset =
    748        float(rand()) / float(RAND_MAX) * 10000.0f + 1000.0f - aState.timestamp;
    749  }
    750  mVRState.timestamp = aState.timestamp + mTimeStampOffset;
    751 
    752  // Avoid division by zero within ConstructProjectionMatrix
    753  const float kEpsilon = 0.00001f;
    754  if (fabs(aDepthFar - aDepthNear) < kEpsilon) {
    755    aDepthFar = aDepthNear + kEpsilon;
    756  }
    757 
    758  const gfx::VRFieldOfView leftFOV =
    759      aInfo.mDisplayState.eyeFOV[gfx::VRDisplayState::Eye_Left];
    760  mLeftProjection =
    761      leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
    762  const gfx::VRFieldOfView rightFOV =
    763      aInfo.mDisplayState.eyeFOV[gfx::VRDisplayState::Eye_Right];
    764  mRightProjection =
    765      rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true);
    766  memcpy(mLeftView.components, aState.leftViewMatrix.data(),
    767         sizeof(aState.leftViewMatrix));
    768  memcpy(mRightView.components, aState.rightViewMatrix.data(),
    769         sizeof(aState.rightViewMatrix));
    770 }
    771 
    772 VRFrameInfo::VRFrameInfo() : mTimeStampOffset(0.0f) {
    773  mVRState.inputFrameID = 0;
    774  mVRState.timestamp = 0.0;
    775  mVRState.flags = gfx::VRDisplayCapabilityFlags::Cap_None;
    776 }
    777 
    778 bool VRFrameInfo::IsDirty() { return mVRState.timestamp == 0; }
    779 
    780 void VRFrameInfo::Clear() { mVRState.Clear(); }
    781 
    782 }  // namespace mozilla::dom