| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/vr/service/browser_xr_runtime.h" |
| |
| #include "base/bind.h" |
| #include "chrome/browser/vr/service/xr_device_impl.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "device/vr/vr_device.h" |
| |
| namespace vr { |
| |
| BrowserXRRuntime::BrowserXRRuntime(device::mojom::XRRuntimePtr runtime, |
| device::mojom::VRDisplayInfoPtr display_info) |
| : runtime_(std::move(runtime)), |
| display_info_(std::move(display_info)), |
| binding_(this), |
| weak_ptr_factory_(this) { |
| device::mojom::XRRuntimeEventListenerAssociatedPtr listener; |
| binding_.Bind(mojo::MakeRequest(&listener)); |
| |
| // Unretained is safe because we are calling through an InterfacePtr we own, |
| // so we won't be called after runtime_ is destroyed. |
| runtime_->ListenToDeviceChanges( |
| listener.PassInterface(), |
| base::BindOnce(&BrowserXRRuntime::OnDisplayInfoChanged, |
| base::Unretained(this))); |
| } |
| |
| BrowserXRRuntime::~BrowserXRRuntime() = default; |
| |
| void BrowserXRRuntime::ExitVrFromPresentingRendererDevice() { |
| auto* xr_device = GetPresentingRendererDevice(); |
| if (xr_device) { |
| xr_device->ExitPresent(); |
| } |
| } |
| |
| void BrowserXRRuntime::OnDisplayInfoChanged( |
| device::mojom::VRDisplayInfoPtr vr_device_info) { |
| bool had_display_info = !!display_info_; |
| display_info_ = std::move(vr_device_info); |
| if (had_display_info) { |
| for (XRDeviceImpl* device : renderer_device_connections_) { |
| device->RuntimesChanged(); |
| } |
| } |
| |
| // Notify observers of the new display info. |
| for (BrowserXRRuntimeObserver& observer : observers_) { |
| observer.SetVRDisplayInfo(display_info_.Clone()); |
| } |
| } |
| |
| void BrowserXRRuntime::StopImmersiveSession() { |
| if (immersive_session_controller_) { |
| immersive_session_controller_ = nullptr; |
| presenting_renderer_device_ = nullptr; |
| |
| for (BrowserXRRuntimeObserver& observer : observers_) { |
| observer.SetWebXRWebContents(nullptr); |
| } |
| } |
| } |
| |
| void BrowserXRRuntime::OnExitPresent() { |
| if (presenting_renderer_device_) { |
| presenting_renderer_device_->OnExitPresent(); |
| presenting_renderer_device_ = nullptr; |
| } |
| } |
| |
| void BrowserXRRuntime::OnDeviceActivated( |
| device::mojom::VRDisplayEventReason reason, |
| base::OnceCallback<void(bool)> on_handled) { |
| if (listening_for_activation_renderer_device_) { |
| listening_for_activation_renderer_device_->OnActivate( |
| reason, std::move(on_handled)); |
| } else { |
| std::move(on_handled).Run(true /* will_not_present */); |
| } |
| } |
| |
| void BrowserXRRuntime::OnDeviceIdle( |
| device::mojom::VRDisplayEventReason reason) { |
| for (XRDeviceImpl* device : renderer_device_connections_) { |
| device->OnDeactivate(reason); |
| } |
| } |
| |
| void BrowserXRRuntime::OnInitialized() { |
| for (auto& callback : pending_initialization_callbacks_) { |
| std::move(callback).Run(display_info_.Clone()); |
| } |
| pending_initialization_callbacks_.clear(); |
| } |
| |
| void BrowserXRRuntime::OnRendererDeviceAdded(XRDeviceImpl* device) { |
| renderer_device_connections_.insert(device); |
| } |
| |
| void BrowserXRRuntime::OnRendererDeviceRemoved(XRDeviceImpl* device) { |
| DCHECK(device); |
| renderer_device_connections_.erase(device); |
| if (device == presenting_renderer_device_) { |
| ExitPresent(device); |
| DCHECK(presenting_renderer_device_ == nullptr); |
| } |
| if (device == listening_for_activation_renderer_device_) { |
| // Not listening for activation. |
| listening_for_activation_renderer_device_ = nullptr; |
| runtime_->SetListeningForActivate(false); |
| } |
| } |
| |
| void BrowserXRRuntime::ExitPresent(XRDeviceImpl* device) { |
| if (device == presenting_renderer_device_) { |
| StopImmersiveSession(); |
| } |
| } |
| |
| void BrowserXRRuntime::RequestSession( |
| XRDeviceImpl* device, |
| const device::mojom::XRRuntimeSessionOptionsPtr& options, |
| device::mojom::XRDevice::RequestSessionCallback callback) { |
| // base::Unretained is safe because we won't be called back after runtime_ is |
| // destroyed. |
| runtime_->RequestSession( |
| options->Clone(), |
| base::BindOnce(&BrowserXRRuntime::OnRequestSessionResult, |
| base::Unretained(this), device->GetWeakPtr(), |
| options->Clone(), std::move(callback))); |
| } |
| |
| void BrowserXRRuntime::OnRequestSessionResult( |
| base::WeakPtr<XRDeviceImpl> device, |
| device::mojom::XRRuntimeSessionOptionsPtr options, |
| device::mojom::XRDevice::RequestSessionCallback callback, |
| device::mojom::XRSessionPtr session, |
| device::mojom::XRSessionControllerPtr immersive_session_controller) { |
| if (session && device) { |
| if (options->immersive) { |
| presenting_renderer_device_ = device.get(); |
| immersive_session_controller_ = std::move(immersive_session_controller); |
| immersive_session_controller_.set_connection_error_handler(base::BindOnce( |
| &BrowserXRRuntime::OnImmersiveSessionError, base::Unretained(this))); |
| |
| // Notify observers that we have started presentation. |
| content::WebContents* web_contents = device->GetWebContents(); |
| for (BrowserXRRuntimeObserver& observer : observers_) { |
| observer.SetWebXRWebContents(web_contents); |
| } |
| } |
| |
| std::move(callback).Run(std::move(session)); |
| } else { |
| std::move(callback).Run(nullptr); |
| if (session) { |
| // The device has been removed, but we still got a session, so make |
| // sure to clean up this weird state. |
| immersive_session_controller_ = std::move(immersive_session_controller); |
| StopImmersiveSession(); |
| } |
| } |
| } |
| |
| void BrowserXRRuntime::OnImmersiveSessionError() { |
| StopImmersiveSession(); |
| } |
| |
| void BrowserXRRuntime::UpdateListeningForActivate(XRDeviceImpl* device) { |
| if (device->ListeningForActivate() && device->InFocusedFrame()) { |
| bool was_listening = !!listening_for_activation_renderer_device_; |
| listening_for_activation_renderer_device_ = device; |
| if (!was_listening) |
| OnListeningForActivate(true); |
| } else if (listening_for_activation_renderer_device_ == device) { |
| listening_for_activation_renderer_device_ = nullptr; |
| OnListeningForActivate(false); |
| } |
| } |
| |
| void BrowserXRRuntime::InitializeAndGetDisplayInfo( |
| content::RenderFrameHost* render_frame_host, |
| device::mojom::XRDevice::GetImmersiveVRDisplayInfoCallback callback) { |
| device::mojom::VRDisplayInfoPtr device_info = GetVRDisplayInfo(); |
| if (device_info) { |
| std::move(callback).Run(std::move(device_info)); |
| return; |
| } |
| |
| int render_process_id = |
| render_frame_host ? render_frame_host->GetProcess()->GetID() : -1; |
| int render_frame_id = |
| render_frame_host ? render_frame_host->GetRoutingID() : -1; |
| pending_initialization_callbacks_.push_back(std::move(callback)); |
| runtime_->EnsureInitialized( |
| render_process_id, render_frame_id, |
| base::BindOnce(&BrowserXRRuntime::OnInitialized, base::Unretained(this))); |
| } |
| |
| void BrowserXRRuntime::OnListeningForActivate(bool is_listening) { |
| runtime_->SetListeningForActivate(is_listening); |
| } |
| |
| } // namespace vr |