Commit 73ed46b8 authored by bajones's avatar bajones Committed by Commit bot

Isolate a presenting VR device from pages other than the one presenting.

Prevents a page from reading or resetting the pose of a
device which is currently being presented to by a
different page, as that potentially creates an
information leak between pages.

Also prevented pages from beginning presentation if another page is already presenting to a device. This is a
little overly aggressive right now, as disallows
presentation to more than one device at a time from any
page, but that's a reasonable limitation at this point
given the rarity of VR hardware.

BUG=389343

Review-Url: https://codereview.chromium.org/2329893002
Cr-Commit-Position: refs/heads/master@{#418771}
parent fd364c78
...@@ -141,15 +141,16 @@ void GvrDevice::ResetPose() { ...@@ -141,15 +141,16 @@ void GvrDevice::ResetPose() {
delegate_->gvr_api()->ResetTracking(); delegate_->gvr_api()->ResetTracking();
} }
void GvrDevice::RequestPresent() { bool GvrDevice::RequestPresent() {
delegate_->RequestWebVRPresent(); delegate_->RequestWebVRPresent();
return true;
} }
void GvrDevice::ExitPresent() { void GvrDevice::ExitPresent() {
delegate_->ExitWebVRPresent(); delegate_->ExitWebVRPresent();
} }
void GvrDevice::SubmitFrame() { void GvrDevice::SubmitFrame(VRPosePtr pose) {
delegate_->SubmitWebVRFrame(); delegate_->SubmitWebVRFrame();
} }
......
...@@ -22,10 +22,10 @@ class GvrDevice : public VRDevice { ...@@ -22,10 +22,10 @@ class GvrDevice : public VRDevice {
VRPosePtr GetPose() override; VRPosePtr GetPose() override;
void ResetPose() override; void ResetPose() override;
void RequestPresent() override; bool RequestPresent() override;
void ExitPresent() override; void ExitPresent() override;
void SubmitFrame() override; void SubmitFrame(VRPosePtr pose) override;
void UpdateLayerBounds(VRLayerBoundsPtr leftBounds, void UpdateLayerBounds(VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds) override; VRLayerBoundsPtr rightBounds) override;
......
...@@ -18,4 +18,8 @@ VRDevice::VRDevice(VRDeviceProvider* provider) ...@@ -18,4 +18,8 @@ VRDevice::VRDevice(VRDeviceProvider* provider)
VRDevice::~VRDevice() {} VRDevice::~VRDevice() {}
bool VRDevice::RequestPresent() {
return true;
};
} // namespace device } // namespace device
...@@ -23,22 +23,21 @@ class VRDeviceProvider; ...@@ -23,22 +23,21 @@ class VRDeviceProvider;
const unsigned int VR_DEVICE_LAST_ID = 0xFFFFFFFF; const unsigned int VR_DEVICE_LAST_ID = 0xFFFFFFFF;
class VRDevice { class DEVICE_VR_EXPORT VRDevice {
public: public:
DEVICE_VR_EXPORT explicit VRDevice(VRDeviceProvider* provider); explicit VRDevice(VRDeviceProvider* provider);
DEVICE_VR_EXPORT virtual ~VRDevice(); virtual ~VRDevice();
DEVICE_VR_EXPORT VRDeviceProvider* provider() const { return provider_; } VRDeviceProvider* provider() const { return provider_; }
DEVICE_VR_EXPORT unsigned int id() const { return id_; } unsigned int id() const { return id_; }
virtual VRDisplayPtr GetVRDevice() = 0; virtual VRDisplayPtr GetVRDevice() = 0;
virtual VRPosePtr GetPose() = 0; virtual VRPosePtr GetPose() = 0;
virtual void ResetPose() = 0; virtual void ResetPose() = 0;
virtual void RequestPresent(){}; virtual bool RequestPresent();
virtual void ExitPresent(){}; virtual void ExitPresent(){};
virtual void SubmitFrame(VRPosePtr pose){};
virtual void SubmitFrame(){};
virtual void UpdateLayerBounds(VRLayerBoundsPtr leftBounds, virtual void UpdateLayerBounds(VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds){}; VRLayerBoundsPtr rightBounds){};
......
...@@ -22,7 +22,11 @@ VRDeviceManager* g_vr_device_manager = nullptr; ...@@ -22,7 +22,11 @@ VRDeviceManager* g_vr_device_manager = nullptr;
} }
VRDeviceManager::VRDeviceManager() VRDeviceManager::VRDeviceManager()
: vr_initialized_(false), keep_alive_(false), has_scheduled_poll_(false) { : vr_initialized_(false),
presenting_service_(nullptr),
presenting_device_(nullptr),
keep_alive_(false),
has_scheduled_poll_(false) {
// Register VRDeviceProviders for the current platform // Register VRDeviceProviders for the current platform
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
RegisterProvider(base::WrapUnique(new GvrDeviceProvider())); RegisterProvider(base::WrapUnique(new GvrDeviceProvider()));
...@@ -30,7 +34,11 @@ VRDeviceManager::VRDeviceManager() ...@@ -30,7 +34,11 @@ VRDeviceManager::VRDeviceManager()
} }
VRDeviceManager::VRDeviceManager(std::unique_ptr<VRDeviceProvider> provider) VRDeviceManager::VRDeviceManager(std::unique_ptr<VRDeviceProvider> provider)
: vr_initialized_(false), keep_alive_(true), has_scheduled_poll_(false) { : vr_initialized_(false),
presenting_service_(nullptr),
presenting_device_(nullptr),
keep_alive_(true),
has_scheduled_poll_(false) {
thread_checker_.DetachFromThread(); thread_checker_.DetachFromThread();
RegisterProvider(std::move(provider)); RegisterProvider(std::move(provider));
SetInstance(this); SetInstance(this);
...@@ -48,6 +56,26 @@ VRDeviceManager* VRDeviceManager::GetInstance() { ...@@ -48,6 +56,26 @@ VRDeviceManager* VRDeviceManager::GetInstance() {
return g_vr_device_manager; return g_vr_device_manager;
} }
// Returns the requested device with the requested id if the specified service
// is allowed to access it.
VRDevice* VRDeviceManager::GetAllowedDevice(VRServiceImpl* service,
unsigned int index) {
VRDeviceManager* device_manager = GetInstance();
// If another service is presenting to the requested device don't allow other
// services to access it. That could potentially allow them to spy on
// where the user is looking on another page, spam another application with
// pose resets, etc.
if (device_manager->presenting_service_ &&
device_manager->presenting_service_ != service) {
if (device_manager->presenting_device_ &&
device_manager->presenting_device_->id() == index)
return nullptr;
}
return device_manager->GetDevice(index);
}
void VRDeviceManager::SetInstance(VRDeviceManager* instance) { void VRDeviceManager::SetInstance(VRDeviceManager* instance) {
// Unit tests can create multiple instances but only one should exist at any // Unit tests can create multiple instances but only one should exist at any
// given time so g_vr_device_manager should only go from nullptr to // given time so g_vr_device_manager should only go from nullptr to
...@@ -72,6 +100,13 @@ void VRDeviceManager::RemoveService(VRServiceImpl* service) { ...@@ -72,6 +100,13 @@ void VRDeviceManager::RemoveService(VRServiceImpl* service) {
services_.erase(std::remove(services_.begin(), services_.end(), service), services_.erase(std::remove(services_.begin(), services_.end(), service),
services_.end()); services_.end());
if (service == presenting_service_) {
presenting_device_->ExitPresent();
presenting_service_ = nullptr;
presenting_device_ = nullptr;
}
if (services_.empty() && !keep_alive_) { if (services_.empty() && !keep_alive_) {
// Delete the device manager when it has no active connections. // Delete the device manager when it has no active connections.
delete g_vr_device_manager; delete g_vr_device_manager;
...@@ -127,6 +162,90 @@ void VRDeviceManager::OnDeviceChanged(VRDisplayPtr device) { ...@@ -127,6 +162,90 @@ void VRDeviceManager::OnDeviceChanged(VRDisplayPtr device) {
service->client()->OnDisplayChanged(device.Clone()); service->client()->OnDisplayChanged(device.Clone());
} }
bool VRDeviceManager::RequestPresent(VRServiceImpl* service,
unsigned int index) {
// Is anything presenting currently?
if (presenting_service_) {
// Should never have a presenting service without a presenting device.
DCHECK(presenting_device_);
// Fail if the currently presenting service is not the one making the
// request.
if (presenting_service_ != service)
return false;
// If we are switching presentation from the currently presenting service to
// a new device stop presening to the previous one.
if (presenting_device_->id() != index) {
// Tell the device to stop presenting.
presenting_device_->ExitPresent();
// Only the presenting service needs to be notified that presentation is
// ending on the previous device.
presenting_service_->client()->OnExitPresent(presenting_device_->id());
presenting_device_ = nullptr;
}
presenting_service_ = nullptr;
}
VRDevice* requested_device = GetDevice(index);
// Can't present to a device that doesn't exist.
if (!requested_device)
return false;
// Attempt to begin presenting to this device. This could fail for any number
// of device-specific reasons.
if (!requested_device->RequestPresent())
return false;
// Successfully began presenting!
presenting_service_ = service;
presenting_device_ = requested_device;
return true;
}
void VRDeviceManager::ExitPresent(VRServiceImpl* service, unsigned int index) {
// Don't allow services other than the currently presenting one to exit
// presentation.
if (presenting_service_ != service)
return;
// Should never have a presenting service without a presenting device.
DCHECK(presenting_device_);
// Fail if the specified device is not currently presenting.
if (presenting_device_->id() != index)
return;
// Tell the device to stop presenting.
presenting_device_->ExitPresent();
presenting_service_->client()->OnExitPresent(index);
// Clear the presenting service and device.
presenting_service_ = nullptr;
presenting_device_ = nullptr;
}
void VRDeviceManager::SubmitFrame(VRServiceImpl* service,
unsigned int index,
VRPosePtr pose) {
// Don't allow services other than the currently presenting one to submit any
// frames.
if (presenting_service_ != service)
return;
// Should never have a presenting service without a presenting device.
DCHECK(presenting_device_);
// Don't submit frames to devices other than the currently presenting one.
if (presenting_device_->id() != index)
return;
presenting_device_->SubmitFrame(std::move(pose));
}
void VRDeviceManager::InitializeProviders() { void VRDeviceManager::InitializeProviders() {
if (vr_initialized_) { if (vr_initialized_) {
return; return;
......
...@@ -32,14 +32,24 @@ class VRDeviceManager : public VRClientDispatcher { ...@@ -32,14 +32,24 @@ class VRDeviceManager : public VRClientDispatcher {
// Returns the VRDeviceManager singleton. // Returns the VRDeviceManager singleton.
static VRDeviceManager* GetInstance(); static VRDeviceManager* GetInstance();
// Gets a VRDevice instance if the specified service is allowed to access it.
DEVICE_VR_EXPORT static VRDevice* GetAllowedDevice(VRServiceImpl* service,
unsigned int index);
// Adds a listener for device manager events. VRDeviceManager does not own // Adds a listener for device manager events. VRDeviceManager does not own
// this object. // this object.
void AddService(VRServiceImpl* service); void AddService(VRServiceImpl* service);
void RemoveService(VRServiceImpl* service); void RemoveService(VRServiceImpl* service);
DEVICE_VR_EXPORT mojo::Array<VRDisplayPtr> GetVRDevices(); DEVICE_VR_EXPORT mojo::Array<VRDisplayPtr> GetVRDevices();
DEVICE_VR_EXPORT VRDevice* GetDevice(unsigned int index);
// Manage presentation to only allow a single service and device at a time.
DEVICE_VR_EXPORT bool RequestPresent(VRServiceImpl* service,
unsigned int index);
DEVICE_VR_EXPORT void ExitPresent(VRServiceImpl* service, unsigned int index);
void SubmitFrame(VRServiceImpl* service, unsigned int index, VRPosePtr pose);
// VRClientDispatcher implementation
void OnDeviceChanged(VRDisplayPtr device) override; void OnDeviceChanged(VRDisplayPtr device) override;
private: private:
...@@ -51,6 +61,8 @@ class VRDeviceManager : public VRClientDispatcher { ...@@ -51,6 +61,8 @@ class VRDeviceManager : public VRClientDispatcher {
DEVICE_VR_EXPORT explicit VRDeviceManager( DEVICE_VR_EXPORT explicit VRDeviceManager(
std::unique_ptr<VRDeviceProvider> provider); std::unique_ptr<VRDeviceProvider> provider);
DEVICE_VR_EXPORT VRDevice* GetDevice(unsigned int index);
static void SetInstance(VRDeviceManager* service); static void SetInstance(VRDeviceManager* service);
static bool HasInstance(); static bool HasInstance();
...@@ -73,6 +85,10 @@ class VRDeviceManager : public VRClientDispatcher { ...@@ -73,6 +85,10 @@ class VRDeviceManager : public VRClientDispatcher {
using ServiceList = std::vector<VRServiceImpl*>; using ServiceList = std::vector<VRServiceImpl*>;
ServiceList services_; ServiceList services_;
// Only one service and device is allowed to present at a time.
VRServiceImpl* presenting_service_;
VRDevice* presenting_device_;
// For testing. If true will not delete self when consumer count reaches 0. // For testing. If true will not delete self when consumer count reaches 0.
bool keep_alive_; bool keep_alive_;
......
...@@ -25,6 +25,10 @@ class VRDeviceManagerTest : public testing::Test { ...@@ -25,6 +25,10 @@ class VRDeviceManagerTest : public testing::Test {
bool HasServiceInstance() { return VRDeviceManager::HasInstance(); } bool HasServiceInstance() { return VRDeviceManager::HasInstance(); }
VRDevice* GetDevice(unsigned int index) {
return device_manager_->GetDevice(index);
}
protected: protected:
FakeVRDeviceProvider* provider_; FakeVRDeviceProvider* provider_;
std::unique_ptr<VRDeviceManager> device_manager_; std::unique_ptr<VRDeviceManager> device_manager_;
...@@ -63,7 +67,7 @@ TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) { ...@@ -63,7 +67,7 @@ TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) {
EXPECT_EQ(0u, webvr_devices.size()); EXPECT_EQ(0u, webvr_devices.size());
// GetDeviceByIndex should return nullptr if an invalid index in queried. // GetDeviceByIndex should return nullptr if an invalid index in queried.
VRDevice* queried_device = device_manager_->GetDevice(1); VRDevice* queried_device = GetDevice(1);
EXPECT_EQ(nullptr, queried_device); EXPECT_EQ(nullptr, queried_device);
std::unique_ptr<FakeVRDevice> device1(new FakeVRDevice(provider_)); std::unique_ptr<FakeVRDevice> device1(new FakeVRDevice(provider_));
...@@ -82,9 +86,9 @@ TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) { ...@@ -82,9 +86,9 @@ TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) {
// NOTE: Returned WebVRDevices are not required to be in any particular order. // NOTE: Returned WebVRDevices are not required to be in any particular order.
// Querying the WebVRDevice index should return the correct device. // Querying the WebVRDevice index should return the correct device.
queried_device = device_manager_->GetDevice(device1->id()); queried_device = GetDevice(device1->id());
EXPECT_EQ(device1.get(), queried_device); EXPECT_EQ(device1.get(), queried_device);
queried_device = device_manager_->GetDevice(device2->id()); queried_device = GetDevice(device2->id());
EXPECT_EQ(device2.get(), queried_device); EXPECT_EQ(device2.get(), queried_device);
provider_->RemoveDevice(device1.get()); provider_->RemoveDevice(device1.get());
......
...@@ -66,15 +66,16 @@ interface VRService { ...@@ -66,15 +66,16 @@ interface VRService {
GetDisplays() => (array<VRDisplay> displays); GetDisplays() => (array<VRDisplay> displays);
[Sync] [Sync]
GetPose(uint32 index) => (VRPose pose); GetPose(uint32 index) => (VRPose? pose);
ResetPose(uint32 index); ResetPose(uint32 index);
RequestPresent(uint32 index); RequestPresent(uint32 index) => (bool success);
ExitPresent(uint32 index); ExitPresent(uint32 index);
SubmitFrame(uint32 index); SubmitFrame(uint32 index, VRPose pose);
UpdateLayerBounds(uint32 index, VRLayerBounds leftBounds, VRLayerBounds rightBounds); UpdateLayerBounds(uint32 index, VRLayerBounds leftBounds, VRLayerBounds rightBounds);
}; };
interface VRServiceClient { interface VRServiceClient {
OnDisplayChanged(VRDisplay display); OnDisplayChanged(VRDisplay display);
OnExitPresent(uint32 index);
}; };
...@@ -48,8 +48,7 @@ void VRServiceImpl::GetDisplays(const GetDisplaysCallback& callback) { ...@@ -48,8 +48,7 @@ void VRServiceImpl::GetDisplays(const GetDisplaysCallback& callback) {
} }
void VRServiceImpl::GetPose(uint32_t index, const GetPoseCallback& callback) { void VRServiceImpl::GetPose(uint32_t index, const GetPoseCallback& callback) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance(); VRDevice* device = VRDeviceManager::GetAllowedDevice(this, index);
VRDevice* device = device_manager->GetDevice(index);
if (device) { if (device) {
callback.Run(device->GetPose()); callback.Run(device->GetPose());
...@@ -59,38 +58,31 @@ void VRServiceImpl::GetPose(uint32_t index, const GetPoseCallback& callback) { ...@@ -59,38 +58,31 @@ void VRServiceImpl::GetPose(uint32_t index, const GetPoseCallback& callback) {
} }
void VRServiceImpl::ResetPose(uint32_t index) { void VRServiceImpl::ResetPose(uint32_t index) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance(); VRDevice* device = VRDeviceManager::GetAllowedDevice(this, index);
VRDevice* device = device_manager->GetDevice(index);
if (device) if (device)
device->ResetPose(); device->ResetPose();
} }
void VRServiceImpl::RequestPresent(uint32_t index) { void VRServiceImpl::RequestPresent(uint32_t index,
const RequestPresentCallback& callback) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance(); VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index); callback.Run(device_manager->RequestPresent(this, index));
if (device)
device->RequestPresent();
} }
void VRServiceImpl::ExitPresent(uint32_t index) { void VRServiceImpl::ExitPresent(uint32_t index) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance(); VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index); device_manager->ExitPresent(this, index);
if (device)
device->ExitPresent();
} }
void VRServiceImpl::SubmitFrame(uint32_t index) { void VRServiceImpl::SubmitFrame(uint32_t index, VRPosePtr pose) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance(); VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index); device_manager->SubmitFrame(this, index, std::move(pose));
if (device)
device->SubmitFrame();
} }
void VRServiceImpl::UpdateLayerBounds(uint32_t index, void VRServiceImpl::UpdateLayerBounds(uint32_t index,
VRLayerBoundsPtr leftBounds, VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds) { VRLayerBoundsPtr rightBounds) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance(); VRDevice* device = VRDeviceManager::GetAllowedDevice(this, index);
VRDevice* device = device_manager->GetDevice(index);
if (device) if (device)
device->UpdateLayerBounds(std::move(leftBounds), std::move(rightBounds)); device->UpdateLayerBounds(std::move(leftBounds), std::move(rightBounds));
} }
......
...@@ -39,9 +39,10 @@ class VRServiceImpl : public VRService { ...@@ -39,9 +39,10 @@ class VRServiceImpl : public VRService {
void GetPose(uint32_t index, const GetPoseCallback& callback) override; void GetPose(uint32_t index, const GetPoseCallback& callback) override;
void ResetPose(uint32_t index) override; void ResetPose(uint32_t index) override;
void RequestPresent(uint32_t index) override; void RequestPresent(uint32_t index,
const RequestPresentCallback& callback) override;
void ExitPresent(uint32_t index) override; void ExitPresent(uint32_t index) override;
void SubmitFrame(uint32_t index) override; void SubmitFrame(uint32_t index, VRPosePtr pose) override;
void UpdateLayerBounds(uint32_t index, void UpdateLayerBounds(uint32_t index,
VRLayerBoundsPtr leftBounds, VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds) override; VRLayerBoundsPtr rightBounds) override;
......
...@@ -25,6 +25,8 @@ class MockVRServiceClient : public VRServiceClient { ...@@ -25,6 +25,8 @@ class MockVRServiceClient : public VRServiceClient {
last_display_ = std::move(display); last_display_ = std::move(display);
} }
MOCK_METHOD1(OnExitPresent, void(uint32_t index));
const VRDisplayPtr& LastDisplay() { return last_display_; } const VRDisplayPtr& LastDisplay() { return last_display_; }
private: private:
...@@ -50,6 +52,7 @@ class VRServiceTestBinding { ...@@ -50,6 +52,7 @@ class VRServiceTestBinding {
} }
MockVRServiceClient& client() { return mock_client_; } MockVRServiceClient& client() { return mock_client_; }
VRServiceImpl* service() { return service_impl_.get(); }
private: private:
std::unique_ptr<VRServiceImpl> service_impl_; std::unique_ptr<VRServiceImpl> service_impl_;
...@@ -81,6 +84,8 @@ class VRServiceImplTest : public testing::Test { ...@@ -81,6 +84,8 @@ class VRServiceImplTest : public testing::Test {
size_t ServiceCount() { return device_manager_->services_.size(); } size_t ServiceCount() { return device_manager_->services_.size(); }
bool presenting() { return !!device_manager_->presenting_service_; }
base::MessageLoop message_loop_; base::MessageLoop message_loop_;
FakeVRDeviceProvider* provider_; FakeVRDeviceProvider* provider_;
std::unique_ptr<VRDeviceManager> device_manager_; std::unique_ptr<VRDeviceManager> device_manager_;
...@@ -126,4 +131,53 @@ TEST_F(VRServiceImplTest, DeviceChangedDispatched) { ...@@ -126,4 +131,53 @@ TEST_F(VRServiceImplTest, DeviceChangedDispatched) {
EXPECT_EQ(device->id(), service_1->client().LastDisplay()->index); EXPECT_EQ(device->id(), service_1->client().LastDisplay()->index);
EXPECT_EQ(device->id(), service_2->client().LastDisplay()->index); EXPECT_EQ(device->id(), service_2->client().LastDisplay()->index);
} }
// Ensure that presenting devices cannot be accessed by other services
TEST_F(VRServiceImplTest, DevicePresentationIsolation) {
std::unique_ptr<VRServiceTestBinding> service_1 = BindService();
std::unique_ptr<VRServiceTestBinding> service_2 = BindService();
std::unique_ptr<FakeVRDevice> device(new FakeVRDevice(provider_));
provider_->AddDevice(device.get());
// Ensure the device manager has seen the fake device
device_manager_->GetVRDevices();
// When not presenting either service should be able to access the device
EXPECT_EQ(device.get(), VRDeviceManager::GetAllowedDevice(
service_1->service(), device->id()));
EXPECT_EQ(device.get(), VRDeviceManager::GetAllowedDevice(
service_2->service(), device->id()));
// Begin presenting to the fake device with service 1
EXPECT_TRUE(
device_manager_->RequestPresent(service_1->service(), device->id()));
EXPECT_TRUE(presenting());
// Service 2 should not be able to present to the device while service 1
// is still presenting.
EXPECT_FALSE(
device_manager_->RequestPresent(service_2->service(), device->id()));
// Only the presenting service should be able to access the device
EXPECT_EQ(device.get(), VRDeviceManager::GetAllowedDevice(
service_1->service(), device->id()));
EXPECT_EQ(nullptr, VRDeviceManager::GetAllowedDevice(service_2->service(),
device->id()));
// Service 2 should not be able to exit presentation to the device
device_manager_->ExitPresent(service_2->service(), device->id());
EXPECT_TRUE(presenting());
// Service 1 should be able to exit the presentation it initiated.
device_manager_->ExitPresent(service_1->service(), device->id());
EXPECT_FALSE(presenting());
// Once presention had ended both services should be able to access the device
EXPECT_EQ(device.get(), VRDeviceManager::GetAllowedDevice(
service_1->service(), device->id()));
EXPECT_EQ(device.get(), VRDeviceManager::GetAllowedDevice(
service_2->service(), device->id()));
}
} }
...@@ -58,12 +58,15 @@ void VRController::resetPose(unsigned index) ...@@ -58,12 +58,15 @@ void VRController::resetPose(unsigned index)
m_service->ResetPose(index); m_service->ResetPose(index);
} }
void VRController::requestPresent(unsigned index) void VRController::requestPresent(ScriptPromiseResolver* resolver, unsigned index)
{ {
if (!m_service) if (!m_service) {
DOMException* exception = DOMException::create(InvalidStateError, "The service is no longer active.");
resolver->reject(exception);
return; return;
}
m_service->RequestPresent(index); m_service->RequestPresent(index, convertToBaseCallback(WTF::bind(&VRController::onPresentComplete, wrapPersistent(this), wrapPersistent(resolver), index)));
} }
void VRController::exitPresent(unsigned index) void VRController::exitPresent(unsigned index)
...@@ -74,12 +77,12 @@ void VRController::exitPresent(unsigned index) ...@@ -74,12 +77,12 @@ void VRController::exitPresent(unsigned index)
m_service->ExitPresent(index); m_service->ExitPresent(index);
} }
void VRController::submitFrame(unsigned index) void VRController::submitFrame(unsigned index, device::blink::VRPosePtr pose)
{ {
if (!m_service) if (!m_service)
return; return;
m_service->SubmitFrame(index); m_service->SubmitFrame(index, std::move(pose));
} }
void VRController::updateLayerBounds(unsigned index, void VRController::updateLayerBounds(unsigned index,
...@@ -140,6 +143,24 @@ void VRController::onGetDisplays(mojo::WTFArray<device::blink::VRDisplayPtr> dis ...@@ -140,6 +143,24 @@ void VRController::onGetDisplays(mojo::WTFArray<device::blink::VRDisplayPtr> dis
callback->onSuccess(outDisplays); callback->onSuccess(outDisplays);
} }
void VRController::onPresentComplete(ScriptPromiseResolver* resolver, unsigned index, bool success)
{
VRDisplay* vrDisplay = getDisplayForIndex(index);
if (!vrDisplay) {
DOMException* exception = DOMException::create(InvalidStateError, "VRDisplay not found.");
resolver->reject(exception);
return;
}
if (success) {
vrDisplay->beginPresent(resolver);
} else {
vrDisplay->forceExitPresent();
DOMException* exception = DOMException::create(NotAllowedError, "Presentation request was denied.");
resolver->reject(exception);
}
}
void VRController::OnDisplayChanged(device::blink::VRDisplayPtr display) void VRController::OnDisplayChanged(device::blink::VRDisplayPtr display)
{ {
VRDisplay* vrDisplay = getDisplayForIndex(display->index); VRDisplay* vrDisplay = getDisplayForIndex(display->index);
...@@ -149,6 +170,13 @@ void VRController::OnDisplayChanged(device::blink::VRDisplayPtr display) ...@@ -149,6 +170,13 @@ void VRController::OnDisplayChanged(device::blink::VRDisplayPtr display)
vrDisplay->update(display); vrDisplay->update(display);
} }
void VRController::OnExitPresent(unsigned index)
{
VRDisplay* vrDisplay = getDisplayForIndex(index);
if (vrDisplay)
vrDisplay->forceExitPresent();
}
void VRController::contextDestroyed() void VRController::contextDestroyed()
{ {
// If the document context was destroyed, shut down the client connection // If the document context was destroyed, shut down the client connection
......
...@@ -34,9 +34,9 @@ public: ...@@ -34,9 +34,9 @@ public:
void getDisplays(ScriptPromiseResolver*); void getDisplays(ScriptPromiseResolver*);
device::blink::VRPosePtr getPose(unsigned index); device::blink::VRPosePtr getPose(unsigned index);
void resetPose(unsigned index); void resetPose(unsigned index);
void requestPresent(unsigned index); void requestPresent(ScriptPromiseResolver*, unsigned index);
void exitPresent(unsigned index); void exitPresent(unsigned index);
void submitFrame(unsigned index); void submitFrame(unsigned index, device::blink::VRPosePtr);
void updateLayerBounds(unsigned index, void updateLayerBounds(unsigned index,
device::blink::VRLayerBoundsPtr leftBounds, device::blink::VRLayerBoundsPtr leftBounds,
device::blink::VRLayerBoundsPtr rightBounds); device::blink::VRLayerBoundsPtr rightBounds);
...@@ -50,9 +50,11 @@ public: ...@@ -50,9 +50,11 @@ public:
private: private:
// Binding callbacks. // Binding callbacks.
void onGetDisplays(mojo::WTFArray<device::blink::VRDisplayPtr>); void onGetDisplays(mojo::WTFArray<device::blink::VRDisplayPtr>);
void onPresentComplete(ScriptPromiseResolver*, unsigned index, bool success);
// VRServiceClient. // VRServiceClient.
void OnDisplayChanged(device::blink::VRDisplayPtr) override; void OnDisplayChanged(device::blink::VRDisplayPtr) override;
void OnExitPresent(unsigned index) override;
// ContextLifecycleObserver. // ContextLifecycleObserver.
void contextDestroyed() override; void contextDestroyed() override;
......
...@@ -160,11 +160,13 @@ ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, const HeapVect ...@@ -160,11 +160,13 @@ ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, const HeapVect
return promise; return promise;
} }
bool firstPresent = !m_isPresenting;
// Initiating VR presentation is only allowed in response to a user gesture. // Initiating VR presentation is only allowed in response to a user gesture.
// If the VRDisplay is already presenting, however, repeated calls are // If the VRDisplay is already presenting, however, repeated calls are
// allowed outside a user gesture so that the presented content may be // allowed outside a user gesture so that the presented content may be
// updated. // updated.
if (!m_isPresenting && !UserGestureIndicator::utilizeUserGesture()) { if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) {
DOMException* exception = DOMException::create(InvalidStateError, "API can only be initiated by a user gesture."); DOMException* exception = DOMException::create(InvalidStateError, "API can only be initiated by a user gesture.");
resolver->reject(exception); resolver->reject(exception);
return promise; return promise;
...@@ -174,73 +176,46 @@ ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, const HeapVect ...@@ -174,73 +176,46 @@ ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, const HeapVect
// A valid number of layers must be provided in order to present. // A valid number of layers must be provided in order to present.
if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) { if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) {
forceExitPresent();
DOMException* exception = DOMException::create(InvalidStateError, "Invalid number of layers."); DOMException* exception = DOMException::create(InvalidStateError, "Invalid number of layers.");
if (m_isPresenting) {
exitPresent(scriptState);
}
resolver->reject(exception); resolver->reject(exception);
return promise; return promise;
} }
m_layer = layers[0]; m_layer = layers[0];
if (m_layer.source()) { if (!m_layer.source()) {
if (!m_capabilities->hasExternalDisplay()) { forceExitPresent();
// TODO: Need a proper VR compositor, but for the moment on mobile DOMException* exception = DOMException::create(InvalidStateError, "Invalid layer source.");
// we'll just make the canvas fullscreen so that VrShell can pick it resolver->reject(exception);
// up through the standard (high latency) compositing path. return promise;
Fullscreen::requestFullscreen(*m_layer.source(), Fullscreen::UnprefixedRequest); }
m_isPresenting = true;
resolver->resolve();
m_navigatorVR->fireVRDisplayPresentChange(this);
// Check to see if the canvas is still the current fullscreen
// element once per second.
m_fullscreenCheckTimer.startRepeating(1.0, BLINK_FROM_HERE);
controller()->requestPresent(m_displayId);
} else {
DOMException* exception = DOMException::create(InvalidStateError, "VR Presentation not implemented for this VRDisplay.");
resolver->reject(exception);
}
// Set up the texture bounds for the provided layer CanvasRenderingContext* renderingContext = m_layer.source()->renderingContext();
device::blink::VRLayerBoundsPtr leftBounds = device::blink::VRLayerBounds::New();
device::blink::VRLayerBoundsPtr rightBounds = device::blink::VRLayerBounds::New();
if (m_layer.hasLeftBounds()) { if (!renderingContext || !renderingContext->is3d()) {
leftBounds->left = m_layer.leftBounds()[0]; forceExitPresent();
leftBounds->top = m_layer.leftBounds()[1]; DOMException* exception = DOMException::create(InvalidStateError, "Layer source must have a WebGLRenderingContext");
leftBounds->width = m_layer.leftBounds()[2]; resolver->reject(exception);
leftBounds->height = m_layer.leftBounds()[3]; return promise;
} else { }
// Left eye defaults
leftBounds->left = 0.0f;
leftBounds->top = 0.0f;
leftBounds->width = 0.5f;
leftBounds->height = 1.0f;
}
if (m_layer.hasRightBounds()) { if (!m_capabilities->hasExternalDisplay()) {
rightBounds->left = m_layer.rightBounds()[0]; // TODO: Need a proper VR compositor, but for the moment on mobile
rightBounds->top = m_layer.rightBounds()[1]; // we'll just make the canvas fullscreen so that VrShell can pick it
rightBounds->width = m_layer.rightBounds()[2]; // up through the standard (high latency) compositing path.
rightBounds->height = m_layer.rightBounds()[3]; Fullscreen::requestFullscreen(*m_layer.source(), Fullscreen::UnprefixedRequest);
} else {
// Right eye defaults // Check to see if the canvas is still the current fullscreen
rightBounds->left = 0.5f; // element once per second.
rightBounds->top = 0.0f; m_fullscreenCheckTimer.startRepeating(1.0, BLINK_FROM_HERE);
rightBounds->width = 0.5f; }
rightBounds->height = 1.0f;
}
controller()->updateLayerBounds(m_displayId, std::move(leftBounds), std::move(rightBounds)); if (firstPresent) {
controller()->requestPresent(resolver, m_displayId);
} else { } else {
DOMException* exception = DOMException::create(InvalidStateError, "Invalid layer source."); updateLayerBounds();
resolver->reject(exception); resolver->resolve();
} }
return promise; return promise;
...@@ -258,22 +233,80 @@ ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState) ...@@ -258,22 +233,80 @@ ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState)
return promise; return promise;
} }
if (!m_capabilities->hasExternalDisplay()) { controller()->exitPresent(m_displayId);
Fullscreen::fullyExitFullscreen(m_layer.source()->document());
m_fullscreenCheckTimer.stop(); resolver->resolve();
controller()->exitPresent(m_displayId);
} else { forceExitPresent();
// Can't get into this presentation mode, so nothing to do here.
return promise;
}
void VRDisplay::beginPresent(ScriptPromiseResolver* resolver)
{
if (m_capabilities->hasExternalDisplay()) {
forceExitPresent();
DOMException* exception = DOMException::create(InvalidStateError, "VR Presentation not implemented for this VRDisplay.");
resolver->reject(exception);
return;
} }
m_isPresenting = false; m_isPresenting = true;
// TODO: Resolve when exit is confirmed updateLayerBounds();
resolver->resolve();
resolver->resolve();
m_navigatorVR->fireVRDisplayPresentChange(this); m_navigatorVR->fireVRDisplayPresentChange(this);
}
return promise; void VRDisplay::forceExitPresent()
{
if (m_isPresenting) {
if (!m_capabilities->hasExternalDisplay()) {
Fullscreen::fullyExitFullscreen(m_layer.source()->document());
m_fullscreenCheckTimer.stop();
} else {
// Can't get into this presentation mode, so nothing to do here.
}
m_navigatorVR->fireVRDisplayPresentChange(this);
}
m_isPresenting = false;
}
void VRDisplay::updateLayerBounds()
{
// Set up the texture bounds for the provided layer
device::blink::VRLayerBoundsPtr leftBounds = device::blink::VRLayerBounds::New();
device::blink::VRLayerBoundsPtr rightBounds = device::blink::VRLayerBounds::New();
if (m_layer.hasLeftBounds()) {
leftBounds->left = m_layer.leftBounds()[0];
leftBounds->top = m_layer.leftBounds()[1];
leftBounds->width = m_layer.leftBounds()[2];
leftBounds->height = m_layer.leftBounds()[3];
} else {
// Left eye defaults
leftBounds->left = 0.0f;
leftBounds->top = 0.0f;
leftBounds->width = 0.5f;
leftBounds->height = 1.0f;
}
if (m_layer.hasRightBounds()) {
rightBounds->left = m_layer.rightBounds()[0];
rightBounds->top = m_layer.rightBounds()[1];
rightBounds->width = m_layer.rightBounds()[2];
rightBounds->height = m_layer.rightBounds()[3];
} else {
// Right eye defaults
rightBounds->left = 0.5f;
rightBounds->top = 0.0f;
rightBounds->width = 0.5f;
rightBounds->height = 1.0f;
}
controller()->updateLayerBounds(m_displayId, std::move(leftBounds), std::move(rightBounds));
} }
HeapVector<VRLayer> VRDisplay::getLayers() HeapVector<VRLayer> VRDisplay::getLayers()
...@@ -289,7 +322,7 @@ HeapVector<VRLayer> VRDisplay::getLayers() ...@@ -289,7 +322,7 @@ HeapVector<VRLayer> VRDisplay::getLayers()
void VRDisplay::submitFrame() void VRDisplay::submitFrame()
{ {
controller()->submitFrame(m_displayId); controller()->submitFrame(m_displayId, m_framePose.Clone());
m_canUpdateFramePose = true; m_canUpdateFramePose = true;
} }
......
...@@ -84,8 +84,14 @@ protected: ...@@ -84,8 +84,14 @@ protected:
VRDisplay(NavigatorVR*); VRDisplay(NavigatorVR*);
void update(const device::blink::VRDisplayPtr&); void update(const device::blink::VRDisplayPtr&);
void updatePose(); void updatePose();
void beginPresent(ScriptPromiseResolver*);
void forceExitPresent();
void updateLayerBounds();
VRController* controller(); VRController* controller();
private: private:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment