Bug 1354206 - Prevent VRDisplay.requestAnimationFrame from succeeding after shutdown
MozReview-Commit-ID: LDw9nH60VCm
--- a/dom/vr/VRDisplay.cpp
+++ b/dom/vr/VRDisplay.cpp
@@ -351,41 +351,46 @@ VRDisplay::WrapObject(JSContext* aCx, JS
}
VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient)
: DOMEventTargetHelper(aWindow)
, mClient(aClient)
, mDepthNear(0.01f) // Default value from WebVR Spec
, mDepthFar(10000.0f) // Default value from WebVR Spec
, mVRNavigationEventDepth(0)
+ , mShutdown(false)
{
const gfx::VRDisplayInfo& info = aClient->GetDisplayInfo();
mDisplayId = info.GetDisplayID();
mDisplayName = NS_ConvertASCIItoUTF16(info.GetDisplayName());
mCapabilities = new VRDisplayCapabilities(aWindow, info.GetCapabilities());
if (info.GetCapabilities() & gfx::VRDisplayCapabilityFlags::Cap_StageParameters) {
mStageParameters = new VRStageParameters(aWindow,
info.GetSittingToStandingTransform(),
info.GetStageSize());
}
mozilla::HoldJSObjects(this);
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (MOZ_LIKELY(obs)) {
+ obs->AddObserver(this, "inner-window-destroyed", false);
+ }
}
VRDisplay::~VRDisplay()
{
- ExitPresentInternal();
+ MOZ_ASSERT(mShutdown);
mozilla::DropJSObjects(this);
}
void
VRDisplay::LastRelease()
{
// We don't want to wait for the CC to free up the presentation
// for use in other documents, so we do this in LastRelease().
- ExitPresentInternal();
+ Shutdown();
}
already_AddRefed<VREyeParameters>
VRDisplay::GetEyeParameters(VREye aEye)
{
gfx::VRDisplayInfo::Eye eye = aEye == VREye::Left ? gfx::VRDisplayInfo::Eye_Left : gfx::VRDisplayInfo::Eye_Right;
RefPtr<VREyeParameters> params =
new VREyeParameters(GetParentObject(),
@@ -489,19 +494,16 @@ VRDisplay::RequestPresent(const nsTArray
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- NS_ENSURE_TRUE(obs, nullptr);
-
if (!EventStateManager::IsHandlingUserInput() &&
aCallerType != CallerType::System &&
!IsHandlingVRNavigationEvent() &&
gfxPrefs::VRRequireGesture()) {
// The WebVR API states that if called outside of a user gesture, the
// promise must be rejected. We allow VR presentations to start within
// trusted events such as vrdisplayactivate, which triggers in response to
// HMD proximity sensors and when navigating within a VR presentation.
@@ -511,24 +513,17 @@ VRDisplay::RequestPresent(const nsTArray
// on a first-come-first-serve basis.
// If this Javascript context is presenting, then we can replace our
// presentation with a new one containing new layers but we should never
// replace the presentation of another context.
promise->MaybeRejectWithUndefined();
} else {
mPresentation = mClient->BeginPresentation(aLayers);
mFrameInfo.Clear();
-
- nsresult rv = obs->AddObserver(this, "inner-window-destroyed", false);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- mPresentation = nullptr;
- promise->MaybeRejectWithUndefined();
- } else {
- promise->MaybeResolve(JS::UndefinedHandleValue);
- }
+ promise->MaybeResolve(JS::UndefinedHandleValue);
}
return promise.forget();
}
NS_IMETHODIMP
VRDisplay::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
@@ -538,17 +533,17 @@ VRDisplay::Observe(nsISupports* aSubject
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
uint64_t innerID;
nsresult rv = wrapper->GetData(&innerID);
NS_ENSURE_SUCCESS(rv, rv);
if (!GetOwner() || GetOwner()->WindowID() == innerID) {
- ExitPresentInternal();
+ Shutdown();
}
return NS_OK;
}
// This should not happen.
return NS_ERROR_FAILURE;
}
@@ -580,16 +575,27 @@ VRDisplay::ExitPresent(ErrorResult& aRv)
void
VRDisplay::ExitPresentInternal()
{
mPresentation = nullptr;
}
void
+VRDisplay::Shutdown()
+{
+ mShutdown = true;
+ ExitPresentInternal();
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (MOZ_LIKELY(obs)) {
+ obs->RemoveObserver(this, "inner-window-destroyed");
+ }
+}
+
+void
VRDisplay::GetLayers(nsTArray<VRLayer>& result)
{
if (mPresentation) {
mPresentation->GetDOMLayers(result);
} else {
result = nsTArray<VRLayer>();
}
}
@@ -602,16 +608,20 @@ VRDisplay::SubmitFrame()
}
mFrameInfo.Clear();
}
int32_t
VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback,
ErrorResult& aError)
{
+ if (mShutdown) {
+ return 0;
+ }
+
gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
int32_t handle;
aError = vm->ScheduleFrameRequestCallback(aCallback, &handle);
return handle;
}
void
--- a/dom/vr/VRDisplay.h
+++ b/dom/vr/VRDisplay.h
@@ -340,16 +340,17 @@ public:
bool IsHandlingVRNavigationEvent();
protected:
VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient);
virtual ~VRDisplay();
virtual void LastRelease() override;
void ExitPresentInternal();
+ void Shutdown();
void UpdateFrameInfo();
RefPtr<gfx::VRDisplayClient> mClient;
uint32_t mDisplayId;
nsString mDisplayName;
RefPtr<VRDisplayCapabilities> mCapabilities;
@@ -367,14 +368,15 @@ protected:
* frame. Subsequent calls before the next SubmitFrame or ExitPresent call
* will use these cached values.
*/
VRFrameInfo mFrameInfo;
// Time at which we began expecting VR navigation.
TimeStamp mHandlingVRNavigationEventStart;
int32_t mVRNavigationEventDepth;
+ bool mShutdown;
};
} // namespace dom
} // namespace mozilla
#endif