Bug 1284357 - Part 2: Implement Navigator.activeVRDisplays
- WebVR 1.0 includes a new property added to Navigator,
activeVRDisplays
- Please apply the patchset in
Bug 1250244 first.
MozReview-Commit-ID: 6wffkwvKllW
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2060,16 +2060,39 @@ Navigator::GetVRDisplays(ErrorResult& aR
return p.forget();
}
mVRGetDisplaysPromises.AppendElement(p);
return p.forget();
}
void
+Navigator::GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const
+{
+ /**
+ * Get only the active VR displays.
+ * Callers do not wish to VRDisplay::RefreshVRDisplays, as the enumeration may
+ * activate hardware that is not yet intended to be used.
+ */
+ if (!mWindow || !mWindow->GetDocShell()) {
+ return;
+ }
+ nsGlobalWindow* win = nsGlobalWindow::Cast(mWindow);
+ win->NotifyVREventListenerAdded();
+ nsTArray<RefPtr<VRDisplay>> displays;
+ if (win->UpdateVRDisplays(displays)) {
+ for (auto display : displays) {
+ if (display->IsPresenting()) {
+ aDisplays.AppendElement(display);
+ }
+ }
+ }
+}
+
+void
Navigator::NotifyVRDisplaysUpdated()
{
// Synchronize the VR devices and resolve the promises in
// mVRGetDisplaysPromises
nsGlobalWindow* win = nsGlobalWindow::Cast(mWindow);
nsTArray<RefPtr<VRDisplay>> vrDisplays;
if (win->UpdateVRDisplays(vrDisplays)) {
@@ -2079,16 +2102,22 @@ Navigator::NotifyVRDisplaysUpdated()
} else {
for (auto p : mVRGetDisplaysPromises) {
p->MaybeReject(NS_ERROR_FAILURE);
}
}
mVRGetDisplaysPromises.Clear();
}
+void
+Navigator::NotifyActiveVRDisplaysChanged()
+{
+ NavigatorBinding::ClearCachedActiveVRDisplaysValue(this);
+}
+
//*****************************************************************************
// Navigator::nsIMozNavigatorNetwork
//*****************************************************************************
NS_IMETHODIMP
Navigator::GetProperties(nsINetworkProperties** aProperties)
{
ErrorResult rv;
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -257,17 +257,17 @@ public:
#ifdef MOZ_B2G_RIL
MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
#endif // MOZ_B2G_RIL
#ifdef MOZ_GAMEPAD
void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
GamepadServiceTest* RequestGamepadServiceTest();
#endif // MOZ_GAMEPAD
already_AddRefed<Promise> GetVRDisplays(ErrorResult& aRv);
- void NotifyVRDisplaysUpdated();
+ void GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const;
#ifdef MOZ_B2G_FM
FMRadio* GetMozFMRadio(ErrorResult& aRv);
#endif
#ifdef MOZ_B2G_BT
bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
#endif // MOZ_B2G_BT
#ifdef MOZ_TIME_MANAGER
time::TimeManager* GetMozTime(ErrorResult& aRv);
@@ -340,16 +340,20 @@ public:
already_AddRefed<Promise>
RequestMediaKeySystemAccess(const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfig,
ErrorResult& aRv);
private:
RefPtr<MediaKeySystemAccessManager> mMediaKeySystemAccessManager;
#endif
+public:
+ void NotifyVRDisplaysUpdated();
+ void NotifyActiveVRDisplaysChanged();
+
private:
virtual ~Navigator();
bool CheckPermission(const char* type);
static bool CheckPermission(nsPIDOMWindowInner* aWindow, const char* aType);
already_AddRefed<nsDOMDeviceStorage> FindDeviceStorage(const nsAString& aName,
const nsAString& aType);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13402,16 +13402,27 @@ nsGlobalWindow::UpdateVRDisplays(nsTArra
{
FORWARD_TO_INNER(UpdateVRDisplays, (aDevices), false);
VRDisplay::UpdateVRDisplays(mVRDisplays, AsInner());
aDevices = mVRDisplays;
return true;
}
+void
+nsGlobalWindow::NotifyActiveVRDisplaysChanged()
+{
+ MOZ_ASSERT(IsInnerWindow());
+
+ if (mNavigator) {
+ mNavigator->NotifyActiveVRDisplaysChanged();
+ }
+}
+
+
// nsGlobalChromeWindow implementation
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
nsGlobalWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -806,16 +806,20 @@ public:
// Inner windows only.
// Enable/disable updates for VR
void EnableVRUpdates();
void DisableVRUpdates();
// Update the VR displays for this window
bool UpdateVRDisplays(nsTArray<RefPtr<mozilla::dom::VRDisplay>>& aDisplays);
+
+ // Inner windows only.
+ // Called to inform that the set of active VR displays has changed.
+ void NotifyActiveVRDisplaysChanged();
#define EVENT(name_, id_, type_, struct_) \
mozilla::dom::EventHandlerNonNull* GetOn##name_() \
{ \
mozilla::EventListenerManager* elm = GetExistingListenerManager(); \
return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString()) \
: nullptr; \
} \
--- a/dom/vr/VREventObserver.cpp
+++ b/dom/vr/VREventObserver.cpp
@@ -36,37 +36,44 @@ VREventObserver::~VREventObserver()
if (vmc) {
vmc->RemoveListener(this);
}
}
void
VREventObserver::NotifyVRDisplayConnect()
{
+ /**
+ * We do not call nsGlobalWindow::NotifyActiveVRDisplaysChanged here, as we
+ * can assume that a newly enumerated display is not presenting WebVR
+ * content.
+ */
if (mWindow->AsInner()->IsCurrentInnerWindow()) {
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
mWindow->GetOuterWindow()->DispatchCustomEvent(
NS_LITERAL_STRING("vrdisplayconnected"));
}
}
void
VREventObserver::NotifyVRDisplayDisconnect()
{
if (mWindow->AsInner()->IsCurrentInnerWindow()) {
+ mWindow->NotifyActiveVRDisplaysChanged();
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
mWindow->GetOuterWindow()->DispatchCustomEvent(
NS_LITERAL_STRING("vrdisplaydisconnected"));
}
}
void
VREventObserver::NotifyVRDisplayPresentChange()
{
if (mWindow->AsInner()->IsCurrentInnerWindow()) {
+ mWindow->NotifyActiveVRDisplaysChanged();
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
mWindow->GetOuterWindow()->DispatchCustomEvent(
NS_LITERAL_STRING("vrdisplaypresentchange"));
}
}
} // namespace dom
} // namespace mozilla
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -210,26 +210,38 @@ VRManager::RefreshVRDisplays(bool aMustD
mLastRefreshTime = TimeStamp::Now();
}
void
VRManager::DispatchVRDisplayInfoUpdate()
{
nsTArray<VRDisplayInfo> update;
- for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
- gfx::VRDisplayHost* display = iter.UserData();
- update.AppendElement(VRDisplayInfo(display->GetDisplayInfo()));
- }
+ GetVRDisplayInfo(update);
for (auto iter = mVRManagerParents.Iter(); !iter.Done(); iter.Next()) {
Unused << iter.Get()->GetKey()->SendUpdateDisplayInfo(update);
}
}
+
+/**
+ * Get any VR displays that have already been enumerated without
+ * activating any new devices.
+ */
+void
+VRManager::GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo)
+{
+ aDisplayInfo.Clear();
+ for (auto iter = mVRDisplays.Iter(); !iter.Done(); iter.Next()) {
+ gfx::VRDisplayHost* display = iter.UserData();
+ aDisplayInfo.AppendElement(VRDisplayInfo(display->GetDisplayInfo()));
+ }
+}
+
RefPtr<gfx::VRDisplayHost>
VRManager::GetDisplay(const uint32_t& aDisplayID)
{
RefPtr<gfx::VRDisplayHost> display;
if (mVRDisplays.Get(aDisplayID, getter_AddRefs(display))) {
return display;
}
return nullptr;
--- a/gfx/vr/VRManager.h
+++ b/gfx/vr/VRManager.h
@@ -33,16 +33,17 @@ public:
void AddVRManagerParent(VRManagerParent* aVRManagerParent);
void RemoveVRManagerParent(VRManagerParent* aVRManagerParent);
void NotifyVsync(const TimeStamp& aVsyncTimestamp);
void NotifyVRVsync(const uint32_t& aDisplayID);
void RefreshVRDisplays(bool aMustDispatch = false);
RefPtr<gfx::VRDisplayHost> GetDisplay(const uint32_t& aDisplayID);
+ void GetVRDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayInfo);
void SubmitFrame(VRLayerParent* aLayer, const int32_t& aInputFrameID,
layers::PTextureParent* aTexture, const gfx::Rect& aLeftEyeRect,
const gfx::Rect& aRightEyeRect);
protected:
VRManager();
~VRManager();
--- a/gfx/vr/ipc/PVRManager.ipdl
+++ b/gfx/vr/ipc/PVRManager.ipdl
@@ -39,16 +39,20 @@ parent:
TextureFlags aTextureFlags, uint64_t aSerial);
async PVRLayer(uint32_t aDisplayID, float aLeftEyeX, float aLeftEyeY, float aLeftEyeWidth, float aLeftEyeHeight, float aRightEyeX, float aRightEyeY, float aRightEyeWidth, float aRightEyeHeight);
// (Re)Enumerate VR Displays. An updated list of VR displays will be returned
// asynchronously to children via UpdateDisplayInfo.
async RefreshDisplays();
+ // GetDisplays synchronously returns the VR displays that have already been
+ // enumerated by RefreshDisplays() but does not enumerate new ones.
+ sync GetDisplays() returns(VRDisplayInfo[] aDisplayInfo);
+
// Reset the sensor of the display identified by aDisplayID so that the current
// sensor state is the "Zero" position.
async ResetSensor(uint32_t aDisplayID);
sync GetSensorState(uint32_t aDisplayID) returns(VRHMDSensorState aState);
sync GetImmediateSensorState(uint32_t aDisplayID) returns(VRHMDSensorState aState);
sync SetHaveEventListener(bool aHaveEventListener);
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -30,16 +30,17 @@ static StaticRefPtr<VRManagerChild> sVRM
static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton;
void ReleaseVRManagerParentSingleton() {
sVRManagerParentSingleton = nullptr;
}
VRManagerChild::VRManagerChild()
: TextureForwarder()
+ , mDisplaysInitialized(false)
, mInputFrameID(-1)
, mMessageLoop(MessageLoop::current())
, mFrameRequestCallbackCounter(0)
, mBackend(layers::LayersBackend::LAYERS_NONE)
{
MOZ_COUNT_CTOR(VRManagerChild);
MOZ_ASSERT(NS_IsMainThread());
@@ -183,18 +184,18 @@ VRManagerChild::AllocPVRLayerChild(const
bool
VRManagerChild::DeallocPVRLayerChild(PVRLayerChild* actor)
{
delete actor;
return true;
}
-bool
-VRManagerChild::RecvUpdateDisplayInfo(nsTArray<VRDisplayInfo>&& aDisplayUpdates)
+void
+VRManagerChild::UpdateDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayUpdates)
{
bool bDisplayConnected = false;
bool bDisplayDisconnected = false;
// Check if any displays have been disconnected
for (auto& display : mDisplays) {
bool found = false;
for (auto& displayUpdate : aDisplayUpdates) {
@@ -207,19 +208,19 @@ VRManagerChild::RecvUpdateDisplayInfo(ns
display->NotifyDisconnected();
bDisplayDisconnected = true;
}
}
// mDisplays could be a hashed container for more scalability, but not worth
// it now as we expect < 10 entries.
nsTArray<RefPtr<VRDisplayClient>> displays;
- for (VRDisplayInfo& displayUpdate: aDisplayUpdates) {
+ for (VRDisplayInfo& displayUpdate : aDisplayUpdates) {
bool isNewDisplay = true;
- for (auto& display: mDisplays) {
+ for (auto& display : mDisplays) {
const VRDisplayInfo& prevInfo = display->GetDisplayInfo();
if (prevInfo.GetDisplayID() == displayUpdate.GetDisplayID()) {
if (displayUpdate.GetIsConnected() && !prevInfo.GetIsConnected()) {
bDisplayConnected = true;
}
if (!displayUpdate.GetIsConnected() && prevInfo.GetIsConnected()) {
bDisplayDisconnected = true;
}
@@ -232,39 +233,56 @@ VRManagerChild::RecvUpdateDisplayInfo(ns
if (isNewDisplay) {
displays.AppendElement(new VRDisplayClient(displayUpdate));
bDisplayConnected = true;
}
}
mDisplays = displays;
- for (auto& nav: mNavigatorCallbacks) {
- // We must call NotifyVRDisplaysUpdated for every
- // Navigator in mNavigatorCallbacks to ensure that
- // the promise returned by Navigator.GetVRDevices
- // can resolve. This must happen even if no changes
- // to VRDisplays have been detected here.
- nav->NotifyVRDisplaysUpdated();
- }
- mNavigatorCallbacks.Clear();
-
if (bDisplayConnected) {
FireDOMVRDisplayConnectEvent();
}
if (bDisplayDisconnected) {
FireDOMVRDisplayDisconnectEvent();
}
+ mDisplaysInitialized = true;
+}
+
+bool
+VRManagerChild::RecvUpdateDisplayInfo(nsTArray<VRDisplayInfo>&& aDisplayUpdates)
+{
+ UpdateDisplayInfo(aDisplayUpdates);
+ for (auto& nav : mNavigatorCallbacks) {
+ /** We must call NotifyVRDisplaysUpdated for every
+ * Navigator in mNavigatorCallbacks to ensure that
+ * the promise returned by Navigator.GetVRDevices
+ * can resolve. This must happen even if no changes
+ * to VRDisplays have been detected here.
+ */
+ nav->NotifyVRDisplaysUpdated();
+ }
+ mNavigatorCallbacks.Clear();
return true;
}
bool
VRManagerChild::GetVRDisplays(nsTArray<RefPtr<VRDisplayClient>>& aDisplays)
{
+ if (!mDisplaysInitialized) {
+ /**
+ * If we haven't received any asynchronous callback after requesting
+ * display enumeration with RefreshDisplays, get the existing displays
+ * that have already been enumerated by other VRManagerChild instances.
+ */
+ nsTArray<VRDisplayInfo> displays;
+ Unused << SendGetDisplays(&displays);
+ UpdateDisplayInfo(displays);
+ }
aDisplays = mDisplays;
return true;
}
bool
VRManagerChild::RefreshVRDisplaysWithCallback(dom::Navigator* aNavigator)
{
bool success = SendRefreshDisplays();
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -67,16 +67,17 @@ public:
virtual MessageLoop* GetMessageLoop() const override { return mMessageLoop; }
virtual base::ProcessId GetParentPid() const override { return OtherPid(); }
nsresult ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback& aCallback,
int32_t *aHandle);
void CancelFrameRequestCallback(int32_t aHandle);
void RunFrameRequestCallbacks();
+ void UpdateDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayUpdates);
void FireDOMVRDisplayConnectEvent();
void FireDOMVRDisplayDisconnectEvent();
void FireDOMVRDisplayPresentChangeEvent();
protected:
explicit VRManagerChild();
~VRManagerChild();
void Destroy();
@@ -137,16 +138,17 @@ private:
void DeliverFence(uint64_t aTextureId, FenceHandle& aReleaseFenceHandle);
/**
* Notify id of Texture When host side end its use. Transaction id is used to
* make sure if there is no newer usage.
*/
void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
nsTArray<RefPtr<VRDisplayClient> > mDisplays;
+ bool mDisplaysInitialized;
nsTArray<dom::Navigator*> mNavigatorCallbacks;
int32_t mInputFrameID;
MessageLoop* mMessageLoop;
struct FrameRequest;
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -241,16 +241,24 @@ VRManagerParent::RecvRefreshDisplays()
// can resolve even if there are no changes to the VR Displays.
VRManager* vm = VRManager::Get();
vm->RefreshVRDisplays(true);
return true;
}
bool
+VRManagerParent::RecvGetDisplays(nsTArray<VRDisplayInfo> *aDisplays)
+{
+ VRManager* vm = VRManager::Get();
+ vm->GetVRDisplayInfo(*aDisplays);
+ return true;
+}
+
+bool
VRManagerParent::RecvResetSensor(const uint32_t& aDisplayID)
{
VRManager* vm = VRManager::Get();
RefPtr<gfx::VRDisplayHost> display = vm->GetDisplay(aDisplayID);
if (display != nullptr) {
display->ZeroSensor();
}
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -80,16 +80,17 @@ protected:
const float& aRightEyeWidth,
const float& aRightEyeHeight) override;
virtual bool DeallocPVRLayerParent(PVRLayerParent* actor) override;
virtual void ActorDestroy(ActorDestroyReason why) override;
void OnChannelConnected(int32_t pid) override;
virtual bool RecvRefreshDisplays() override;
+ virtual bool RecvGetDisplays(nsTArray<VRDisplayInfo> *aDisplays) override;
virtual bool RecvResetSensor(const uint32_t& aDisplayID) override;
virtual bool RecvGetSensorState(const uint32_t& aDisplayID, VRHMDSensorState* aState) override;
virtual bool RecvGetImmediateSensorState(const uint32_t& aDisplayID, VRHMDSensorState* aState) override;
virtual bool RecvSetHaveEventListener(const bool& aHaveEventListener) override;
private:
void RegisterWithManager();
void UnregisterFromManager();