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() {
delegate_->gvr_api()->ResetTracking();
}
void GvrDevice::RequestPresent() {
bool GvrDevice::RequestPresent() {
delegate_->RequestWebVRPresent();
return true;
}
void GvrDevice::ExitPresent() {
delegate_->ExitWebVRPresent();
}
void GvrDevice::SubmitFrame() {
void GvrDevice::SubmitFrame(VRPosePtr pose) {
delegate_->SubmitWebVRFrame();
}
......
......@@ -22,10 +22,10 @@ class GvrDevice : public VRDevice {
VRPosePtr GetPose() override;
void ResetPose() override;
void RequestPresent() override;
bool RequestPresent() override;
void ExitPresent() override;
void SubmitFrame() override;
void SubmitFrame(VRPosePtr pose) override;
void UpdateLayerBounds(VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds) override;
......
......@@ -18,4 +18,8 @@ VRDevice::VRDevice(VRDeviceProvider* provider)
VRDevice::~VRDevice() {}
bool VRDevice::RequestPresent() {
return true;
};
} // namespace device
......@@ -23,22 +23,21 @@ class VRDeviceProvider;
const unsigned int VR_DEVICE_LAST_ID = 0xFFFFFFFF;
class VRDevice {
class DEVICE_VR_EXPORT VRDevice {
public:
DEVICE_VR_EXPORT explicit VRDevice(VRDeviceProvider* provider);
DEVICE_VR_EXPORT virtual ~VRDevice();
explicit VRDevice(VRDeviceProvider* provider);
virtual ~VRDevice();
DEVICE_VR_EXPORT VRDeviceProvider* provider() const { return provider_; }
DEVICE_VR_EXPORT unsigned int id() const { return id_; }
VRDeviceProvider* provider() const { return provider_; }
unsigned int id() const { return id_; }
virtual VRDisplayPtr GetVRDevice() = 0;
virtual VRPosePtr GetPose() = 0;
virtual void ResetPose() = 0;
virtual void RequestPresent(){};
virtual bool RequestPresent();
virtual void ExitPresent(){};
virtual void SubmitFrame(){};
virtual void SubmitFrame(VRPosePtr pose){};
virtual void UpdateLayerBounds(VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds){};
......
......@@ -22,7 +22,11 @@ VRDeviceManager* g_vr_device_manager = nullptr;
}
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
#if defined(OS_ANDROID)
RegisterProvider(base::WrapUnique(new GvrDeviceProvider()));
......@@ -30,7 +34,11 @@ VRDeviceManager::VRDeviceManager()
}
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();
RegisterProvider(std::move(provider));
SetInstance(this);
......@@ -48,6 +56,26 @@ VRDeviceManager* VRDeviceManager::GetInstance() {
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) {
// 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
......@@ -72,6 +100,13 @@ void VRDeviceManager::RemoveService(VRServiceImpl* service) {
services_.erase(std::remove(services_.begin(), services_.end(), service),
services_.end());
if (service == presenting_service_) {
presenting_device_->ExitPresent();
presenting_service_ = nullptr;
presenting_device_ = nullptr;
}
if (services_.empty() && !keep_alive_) {
// Delete the device manager when it has no active connections.
delete g_vr_device_manager;
......@@ -127,6 +162,90 @@ void VRDeviceManager::OnDeviceChanged(VRDisplayPtr device) {
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() {
if (vr_initialized_) {
return;
......
......@@ -32,14 +32,24 @@ class VRDeviceManager : public VRClientDispatcher {
// Returns the VRDeviceManager singleton.
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
// this object.
void AddService(VRServiceImpl* service);
void RemoveService(VRServiceImpl* service);
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;
private:
......@@ -51,6 +61,8 @@ class VRDeviceManager : public VRClientDispatcher {
DEVICE_VR_EXPORT explicit VRDeviceManager(
std::unique_ptr<VRDeviceProvider> provider);
DEVICE_VR_EXPORT VRDevice* GetDevice(unsigned int index);
static void SetInstance(VRDeviceManager* service);
static bool HasInstance();
......@@ -73,6 +85,10 @@ class VRDeviceManager : public VRClientDispatcher {
using ServiceList = std::vector<VRServiceImpl*>;
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.
bool keep_alive_;
......
......@@ -25,6 +25,10 @@ class VRDeviceManagerTest : public testing::Test {
bool HasServiceInstance() { return VRDeviceManager::HasInstance(); }
VRDevice* GetDevice(unsigned int index) {
return device_manager_->GetDevice(index);
}
protected:
FakeVRDeviceProvider* provider_;
std::unique_ptr<VRDeviceManager> device_manager_;
......@@ -63,7 +67,7 @@ TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) {
EXPECT_EQ(0u, webvr_devices.size());
// 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);
std::unique_ptr<FakeVRDevice> device1(new FakeVRDevice(provider_));
......@@ -82,9 +86,9 @@ TEST_F(VRDeviceManagerTest, GetDevicesBasicTest) {
// NOTE: Returned WebVRDevices are not required to be in any particular order.
// 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);
queried_device = device_manager_->GetDevice(device2->id());
queried_device = GetDevice(device2->id());
EXPECT_EQ(device2.get(), queried_device);
provider_->RemoveDevice(device1.get());
......
......@@ -66,15 +66,16 @@ interface VRService {
GetDisplays() => (array<VRDisplay> displays);
[Sync]
GetPose(uint32 index) => (VRPose pose);
GetPose(uint32 index) => (VRPose? pose);
ResetPose(uint32 index);
RequestPresent(uint32 index);
RequestPresent(uint32 index) => (bool success);
ExitPresent(uint32 index);
SubmitFrame(uint32 index);
SubmitFrame(uint32 index, VRPose pose);
UpdateLayerBounds(uint32 index, VRLayerBounds leftBounds, VRLayerBounds rightBounds);
};
interface VRServiceClient {
OnDisplayChanged(VRDisplay display);
OnExitPresent(uint32 index);
};
......@@ -48,8 +48,7 @@ void VRServiceImpl::GetDisplays(const GetDisplaysCallback& callback) {
}
void VRServiceImpl::GetPose(uint32_t index, const GetPoseCallback& callback) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index);
VRDevice* device = VRDeviceManager::GetAllowedDevice(this, index);
if (device) {
callback.Run(device->GetPose());
......@@ -59,38 +58,31 @@ void VRServiceImpl::GetPose(uint32_t index, const GetPoseCallback& callback) {
}
void VRServiceImpl::ResetPose(uint32_t index) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index);
VRDevice* device = VRDeviceManager::GetAllowedDevice(this, index);
if (device)
device->ResetPose();
}
void VRServiceImpl::RequestPresent(uint32_t index) {
void VRServiceImpl::RequestPresent(uint32_t index,
const RequestPresentCallback& callback) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index);
if (device)
device->RequestPresent();
callback.Run(device_manager->RequestPresent(this, index));
}
void VRServiceImpl::ExitPresent(uint32_t index) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index);
if (device)
device->ExitPresent();
device_manager->ExitPresent(this, index);
}
void VRServiceImpl::SubmitFrame(uint32_t index) {
void VRServiceImpl::SubmitFrame(uint32_t index, VRPosePtr pose) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index);
if (device)
device->SubmitFrame();
device_manager->SubmitFrame(this, index, std::move(pose));
}
void VRServiceImpl::UpdateLayerBounds(uint32_t index,
VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds) {
VRDeviceManager* device_manager = VRDeviceManager::GetInstance();
VRDevice* device = device_manager->GetDevice(index);
VRDevice* device = VRDeviceManager::GetAllowedDevice(this, index);
if (device)
device->UpdateLayerBounds(std::move(leftBounds), std::move(rightBounds));
}
......
......@@ -39,9 +39,10 @@ class VRServiceImpl : public VRService {
void GetPose(uint32_t index, const GetPoseCallback& callback) 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 SubmitFrame(uint32_t index) override;
void SubmitFrame(uint32_t index, VRPosePtr pose) override;
void UpdateLayerBounds(uint32_t index,
VRLayerBoundsPtr leftBounds,
VRLayerBoundsPtr rightBounds) override;
......
......@@ -25,6 +25,8 @@ class MockVRServiceClient : public VRServiceClient {
last_display_ = std::move(display);
}
MOCK_METHOD1(OnExitPresent, void(uint32_t index));
const VRDisplayPtr& LastDisplay() { return last_display_; }
private:
......@@ -50,6 +52,7 @@ class VRServiceTestBinding {
}
MockVRServiceClient& client() { return mock_client_; }
VRServiceImpl* service() { return service_impl_.get(); }
private:
std::unique_ptr<VRServiceImpl> service_impl_;
......@@ -81,6 +84,8 @@ class VRServiceImplTest : public testing::Test {
size_t ServiceCount() { return device_manager_->services_.size(); }
bool presenting() { return !!device_manager_->presenting_service_; }
base::MessageLoop message_loop_;
FakeVRDeviceProvider* provider_;
std::unique_ptr<VRDeviceManager> device_manager_;
......@@ -126,4 +131,53 @@ TEST_F(VRServiceImplTest, DeviceChangedDispatched) {
EXPECT_EQ(device->id(), service_1->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)
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;
}
m_service->RequestPresent(index);
m_service->RequestPresent(index, convertToBaseCallback(WTF::bind(&VRController::onPresentComplete, wrapPersistent(this), wrapPersistent(resolver), index)));
}
void VRController::exitPresent(unsigned index)
......@@ -74,12 +77,12 @@ void VRController::exitPresent(unsigned index)
m_service->ExitPresent(index);
}
void VRController::submitFrame(unsigned index)
void VRController::submitFrame(unsigned index, device::blink::VRPosePtr pose)
{
if (!m_service)
return;
m_service->SubmitFrame(index);
m_service->SubmitFrame(index, std::move(pose));
}
void VRController::updateLayerBounds(unsigned index,
......@@ -140,6 +143,24 @@ void VRController::onGetDisplays(mojo::WTFArray<device::blink::VRDisplayPtr> dis
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)
{
VRDisplay* vrDisplay = getDisplayForIndex(display->index);
......@@ -149,6 +170,13 @@ void VRController::OnDisplayChanged(device::blink::VRDisplayPtr display)
vrDisplay->update(display);
}
void VRController::OnExitPresent(unsigned index)
{
VRDisplay* vrDisplay = getDisplayForIndex(index);
if (vrDisplay)
vrDisplay->forceExitPresent();
}
void VRController::contextDestroyed()
{
// If the document context was destroyed, shut down the client connection
......
......@@ -34,9 +34,9 @@ public:
void getDisplays(ScriptPromiseResolver*);
device::blink::VRPosePtr getPose(unsigned index);
void resetPose(unsigned index);
void requestPresent(unsigned index);
void requestPresent(ScriptPromiseResolver*, unsigned index);
void exitPresent(unsigned index);
void submitFrame(unsigned index);
void submitFrame(unsigned index, device::blink::VRPosePtr);
void updateLayerBounds(unsigned index,
device::blink::VRLayerBoundsPtr leftBounds,
device::blink::VRLayerBoundsPtr rightBounds);
......@@ -50,9 +50,11 @@ public:
private:
// Binding callbacks.
void onGetDisplays(mojo::WTFArray<device::blink::VRDisplayPtr>);
void onPresentComplete(ScriptPromiseResolver*, unsigned index, bool success);
// VRServiceClient.
void OnDisplayChanged(device::blink::VRDisplayPtr) override;
void OnExitPresent(unsigned index) override;
// ContextLifecycleObserver.
void contextDestroyed() override;
......
......@@ -160,11 +160,13 @@ ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, const HeapVect
return promise;
}
bool firstPresent = !m_isPresenting;
// Initiating VR presentation is only allowed in response to a user gesture.
// If the VRDisplay is already presenting, however, repeated calls are
// allowed outside a user gesture so that the presented content may be
// updated.
if (!m_isPresenting && !UserGestureIndicator::utilizeUserGesture()) {
if (firstPresent && !UserGestureIndicator::utilizeUserGesture()) {
DOMException* exception = DOMException::create(InvalidStateError, "API can only be initiated by a user gesture.");
resolver->reject(exception);
return promise;
......@@ -174,73 +176,46 @@ ScriptPromise VRDisplay::requestPresent(ScriptState* scriptState, const HeapVect
// A valid number of layers must be provided in order to present.
if (layers.size() == 0 || layers.size() > m_capabilities->maxLayers()) {
forceExitPresent();
DOMException* exception = DOMException::create(InvalidStateError, "Invalid number of layers.");
if (m_isPresenting) {
exitPresent(scriptState);
}
resolver->reject(exception);
return promise;
}
m_layer = layers[0];
if (m_layer.source()) {
if (!m_capabilities->hasExternalDisplay()) {
// TODO: Need a proper VR compositor, but for the moment on mobile
// we'll just make the canvas fullscreen so that VrShell can pick it
// up through the standard (high latency) compositing path.
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);
}
if (!m_layer.source()) {
forceExitPresent();
DOMException* exception = DOMException::create(InvalidStateError, "Invalid layer source.");
resolver->reject(exception);
return promise;
}
// 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();
CanvasRenderingContext* renderingContext = m_layer.source()->renderingContext();
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 (!renderingContext || !renderingContext->is3d()) {
forceExitPresent();
DOMException* exception = DOMException::create(InvalidStateError, "Layer source must have a WebGLRenderingContext");
resolver->reject(exception);
return promise;
}
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;
}
if (!m_capabilities->hasExternalDisplay()) {
// TODO: Need a proper VR compositor, but for the moment on mobile
// we'll just make the canvas fullscreen so that VrShell can pick it
// up through the standard (high latency) compositing path.
Fullscreen::requestFullscreen(*m_layer.source(), Fullscreen::UnprefixedRequest);
// Check to see if the canvas is still the current fullscreen
// element once per second.
m_fullscreenCheckTimer.startRepeating(1.0, BLINK_FROM_HERE);
}
controller()->updateLayerBounds(m_displayId, std::move(leftBounds), std::move(rightBounds));
if (firstPresent) {
controller()->requestPresent(resolver, m_displayId);
} else {
DOMException* exception = DOMException::create(InvalidStateError, "Invalid layer source.");
resolver->reject(exception);
updateLayerBounds();
resolver->resolve();
}
return promise;
......@@ -258,22 +233,80 @@ ScriptPromise VRDisplay::exitPresent(ScriptState* scriptState)
return promise;
}
if (!m_capabilities->hasExternalDisplay()) {
Fullscreen::fullyExitFullscreen(m_layer.source()->document());
m_fullscreenCheckTimer.stop();
controller()->exitPresent(m_displayId);
} else {
// Can't get into this presentation mode, so nothing to do here.
controller()->exitPresent(m_displayId);
resolver->resolve();
forceExitPresent();
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
resolver->resolve();
updateLayerBounds();
resolver->resolve();
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()
......@@ -289,7 +322,7 @@ HeapVector<VRLayer> VRDisplay::getLayers()
void VRDisplay::submitFrame()
{
controller()->submitFrame(m_displayId);
controller()->submitFrame(m_displayId, m_framePose.Clone());
m_canUpdateFramePose = true;
}
......
......@@ -84,8 +84,14 @@ protected:
VRDisplay(NavigatorVR*);
void update(const device::blink::VRDisplayPtr&);
void updatePose();
void beginPresent(ScriptPromiseResolver*);
void forceExitPresent();
void updateLayerBounds();
VRController* controller();
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