| // Copyright 2016 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/android/vr/vr_shell.h" |
| |
| #include <android/native_window_jni.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <utility> |
| |
| #include "base/android/jni_string.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/values.h" |
| #include "chrome/browser/android/chrome_feature_list.h" |
| #include "chrome/browser/android/tab_android.h" |
| #include "chrome/browser/android/vr/android_ui_gesture_target.h" |
| #include "chrome/browser/android/vr/autocomplete_controller.h" |
| #include "chrome/browser/android/vr/vr_gl_thread.h" |
| #include "chrome/browser/android/vr/vr_input_connection.h" |
| #include "chrome/browser/android/vr/vr_shell_delegate.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/component_updater/vr_assets_component_installer.h" |
| #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" |
| #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h" |
| #include "chrome/browser/permissions/permission_manager.h" |
| #include "chrome/browser/permissions/permission_result.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ssl/security_state_tab_helper.h" |
| #include "chrome/browser/vr/assets_loader.h" |
| #include "chrome/browser/vr/browser_renderer.h" |
| #include "chrome/browser/vr/metrics/metrics_helper.h" |
| #include "chrome/browser/vr/metrics/session_metrics_helper.h" |
| #include "chrome/browser/vr/model/assets.h" |
| #include "chrome/browser/vr/model/omnibox_suggestions.h" |
| #include "chrome/browser/vr/model/text_input_info.h" |
| #include "chrome/browser/vr/toolbar_helper.h" |
| #include "chrome/browser/vr/ui_test_input.h" |
| #include "chrome/browser/vr/vr_tab_helper.h" |
| #include "chrome/browser/vr/vr_web_contents_observer.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host.h" |
| #include "content/public/browser/render_widget_host_iterator.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/referrer.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/common/url_constants.h" |
| #include "device/vr/android/gvr/cardboard_gamepad_data_fetcher.h" |
| #include "device/vr/android/gvr/gvr_device.h" |
| #include "device/vr/android/gvr/gvr_gamepad_data_fetcher.h" |
| #include "gpu/command_buffer/common/mailbox.h" |
| #include "jni/VrShell_jni.h" |
| #include "services/device/public/mojom/constants.mojom.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "ui/android/window_android.h" |
| #include "ui/base/page_transition_types.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/gfx/android/java_bitmap.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/geometry/point_f.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/geometry/size_conversions.h" |
| #include "ui/gl/android/surface_texture.h" |
| #include "url/gurl.h" |
| |
| using base::android::JavaParamRef; |
| using base::android::ScopedJavaLocalRef; |
| |
| namespace vr { |
| |
| namespace { |
| vr::VrShell* g_vr_shell_instance; |
| |
| constexpr base::TimeDelta kPollCapturingStateInterval = |
| base::TimeDelta::FromSecondsD(0.2); |
| |
| constexpr base::TimeDelta kAssetsComponentWaitDelay = |
| base::TimeDelta::FromSeconds(2); |
| |
| static constexpr float kInchesToMeters = 0.0254f; |
| // Screen pixel density of the Google Pixel phone in pixels per inch. |
| static constexpr float kPixelPpi = 441.0f; |
| // Screen pixel density of the Google Pixel phone in pixels per meter. |
| static constexpr float kPixelPpm = kPixelPpi / kInchesToMeters; |
| |
| // Factor to adjust the legibility of the content. Making this factor smaller |
| // increases the text size. |
| static constexpr float kContentLegibilityFactor = 1.36f; |
| |
| // This factor converts the physical width of the projected |
| // content quad into the required width of the virtual content window. |
| // TODO(tiborg): This value is calibrated for the Google Pixel. We should adjust |
| // this value dynamically based on the target device's pixel density in the |
| // future. |
| static constexpr float kContentBoundsMetersToWindowSize = |
| kPixelPpm * kContentLegibilityFactor; |
| |
| // Factor by which the content's pixel amount is increased beyond what the |
| // projected content quad covers in screen real estate. |
| // This DPR factor works well on Pixel phones. |
| static constexpr float kContentDprFactor = 4.0f; |
| |
| void SetIsInVR(content::WebContents* contents, bool is_in_vr) { |
| if (contents && contents->GetRenderWidgetHostView()) { |
| // TODO(asimjour) Contents should not be aware of VR mode. Instead, we |
| // should add a flag for disabling specific UI such as the keyboard (see |
| // VrTabHelper for details). |
| contents->GetRenderWidgetHostView()->SetIsInVR(is_in_vr); |
| |
| VrTabHelper* vr_tab_helper = VrTabHelper::FromWebContents(contents); |
| DCHECK(vr_tab_helper); |
| vr_tab_helper->SetIsInVr(is_in_vr); |
| } |
| } |
| |
| } // namespace |
| |
| VrShell::VrShell(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const UiInitialState& ui_initial_state, |
| VrShellDelegate* delegate, |
| gvr_context* gvr_api, |
| bool reprojected_rendering, |
| float display_width_meters, |
| float display_height_meters, |
| int display_width_pixels, |
| int display_height_pixels, |
| bool pause_content, |
| bool low_density) |
| : web_vr_autopresentation_expected_( |
| ui_initial_state.web_vr_autopresentation_expected), |
| delegate_provider_(delegate), |
| main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| reprojected_rendering_(reprojected_rendering), |
| display_size_meters_(display_width_meters, display_height_meters), |
| display_size_pixels_(display_width_pixels, display_height_pixels), |
| gl_surface_created_event_( |
| base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED), |
| weak_ptr_factory_(this) { |
| DVLOG(1) << __FUNCTION__ << "=" << this; |
| DCHECK(g_vr_shell_instance == nullptr); |
| g_vr_shell_instance = this; |
| j_vr_shell_.Reset(env, obj); |
| |
| base::OnceCallback<gfx::AcceleratedWidget()> surface_callback = |
| base::BindOnce(&VrShell::GetRenderSurface, base::Unretained(this)); |
| |
| gl_thread_ = std::make_unique<VrGLThread>( |
| weak_ptr_factory_.GetWeakPtr(), main_thread_task_runner_, gvr_api, |
| ui_initial_state, reprojected_rendering_, HasDaydreamSupport(env), |
| pause_content, low_density, &gl_surface_created_event_, |
| std::move(surface_callback)); |
| ui_ = gl_thread_.get(); |
| toolbar_ = std::make_unique<ToolbarHelper>(ui_, this); |
| autocomplete_controller_ = |
| std::make_unique<AutocompleteController>(base::BindRepeating( |
| &BrowserUiInterface::SetOmniboxSuggestions, base::Unretained(ui_))); |
| |
| gl_thread_->Start(); |
| |
| if (ui_initial_state.in_web_vr || |
| ui_initial_state.web_vr_autopresentation_expected) { |
| UMA_HISTOGRAM_BOOLEAN("VRAutopresentedWebVR", |
| ui_initial_state.web_vr_autopresentation_expected); |
| } |
| |
| can_load_new_assets_ = AssetsLoader::GetInstance()->ComponentReady(); |
| if (!can_load_new_assets_) { |
| waiting_for_assets_component_timer_.Start( |
| FROM_HERE, kAssetsComponentWaitDelay, |
| base::BindRepeating(&VrShell::OnAssetsComponentWaitTimeout, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| AssetsLoader::GetInstance()->SetOnComponentReadyCallback(base::BindRepeating( |
| &VrShell::OnAssetsComponentReady, weak_ptr_factory_.GetWeakPtr())); |
| AssetsLoader::GetInstance()->GetMetricsHelper()->OnEnter(Mode::kVr); |
| |
| UpdateVrAssetsComponent(g_browser_process->component_updater()); |
| |
| auto* connector = |
| content::ServiceManagerConnection::GetForProcess()->GetConnector(); |
| connector->BindInterface(device::mojom::kServiceName, &geolocation_config_); |
| } |
| |
| void VrShell::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| delete this; |
| } |
| |
| bool VrShell::HasUiFinishedLoading( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| return ui_finished_loading_; |
| } |
| |
| void VrShell::SwapContents(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& tab) { |
| content_id_++; |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::OnSwapContents, |
| gl_thread_->GetBrowserRenderer(), content_id_)); |
| TabAndroid* active_tab = |
| tab.is_null() |
| ? nullptr |
| : TabAndroid::GetNativeTab(env, JavaParamRef<jobject>(env, tab)); |
| |
| content::WebContents* contents = |
| active_tab ? active_tab->web_contents() : nullptr; |
| bool is_native_page = active_tab ? active_tab->IsNativePage() : true; |
| |
| SetIsInVR(GetNonNativePageWebContents(), false); |
| |
| web_contents_ = contents; |
| web_contents_is_native_page_ = is_native_page; |
| SetIsInVR(GetNonNativePageWebContents(), true); |
| SetUiState(); |
| |
| if (web_contents_) { |
| vr_input_connection_.reset(new VrInputConnection(web_contents_)); |
| PostToGlThread(FROM_HERE, base::BindOnce(&VrGLThread::SetInputConnection, |
| base::Unretained(gl_thread_.get()), |
| vr_input_connection_.get())); |
| } else { |
| vr_input_connection_ = nullptr; |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&VrGLThread::SetInputConnection, |
| base::Unretained(gl_thread_.get()), nullptr)); |
| } |
| |
| vr_web_contents_observer_ = std::make_unique<VrWebContentsObserver>( |
| web_contents_, ui_, toolbar_.get(), |
| base::BindOnce(&VrShell::ContentWebContentsDestroyed, |
| base::Unretained(this))); |
| |
| // TODO(https://crbug.com/684661): Make SessionMetricsHelper tab-aware and |
| // able to track multiple tabs. |
| if (web_contents_ && !SessionMetricsHelper::FromWebContents(web_contents_)) { |
| SessionMetricsHelper::CreateForWebContents( |
| web_contents_, |
| webvr_mode_ ? Mode::kWebXrVrPresentation : Mode::kVrBrowsingRegular, |
| web_vr_autopresentation_expected_); |
| } |
| } |
| |
| void VrShell::SetAndroidGestureTarget( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& android_ui_gesture_target) { |
| android_ui_gesture_target_.reset( |
| AndroidUiGestureTarget::FromJavaObject(android_ui_gesture_target)); |
| } |
| |
| void VrShell::SetDialogGestureTarget( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& dialog_gesture_target) { |
| dialog_gesture_target_.reset( |
| AndroidUiGestureTarget::FromJavaObject(dialog_gesture_target)); |
| } |
| |
| void VrShell::SetUiState() { |
| toolbar_->Update(); |
| |
| if (!GetNonNativePageWebContents()) { |
| ui_->SetLoading(false); |
| ui_->SetFullscreen(false); |
| } else { |
| ui_->SetLoading(GetNonNativePageWebContents()->IsLoading()); |
| ui_->SetFullscreen(GetNonNativePageWebContents()->IsFullscreen()); |
| } |
| if (web_contents_) { |
| ui_->SetIncognito(web_contents_->GetBrowserContext()->IsOffTheRecord()); |
| } else { |
| ui_->SetIncognito(false); |
| } |
| } |
| |
| VrShell::~VrShell() { |
| DVLOG(1) << __FUNCTION__ << "=" << this; |
| content_surface_texture_ = nullptr; |
| overlay_surface_texture_ = nullptr; |
| if (gvr_gamepad_source_active_) { |
| device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory( |
| device::GAMEPAD_SOURCE_GVR); |
| } |
| |
| if (cardboard_gamepad_source_active_) { |
| device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory( |
| device::GAMEPAD_SOURCE_CARDBOARD); |
| } |
| |
| delegate_provider_->RemoveDelegate(); |
| { |
| // The GvrLayout is, and must always be, used only on the UI thread, and the |
| // GvrApi used for rendering should only be used from the GL thread as it's |
| // not thread safe. However, the GvrLayout owns the GvrApi instance, and |
| // when it gets shut down it deletes the GvrApi instance with it. Therefore, |
| // we need to block shutting down the GvrLayout on stopping our GL thread |
| // from using the GvrApi instance. |
| // base::Thread::Stop, which is called when destroying the thread, asserts |
| // that IO is allowed to prevent jank, but there shouldn't be any concerns |
| // regarding jank in this case, because we're switching from 3D to 2D, |
| // adding/removing a bunch of Java views, and probably changing device |
| // orientation here. |
| base::ThreadRestrictions::ScopedAllowIO allow_io; |
| gl_thread_.reset(); |
| } |
| g_vr_shell_instance = nullptr; |
| } |
| |
| void VrShell::PostToGlThread(const base::Location& from_here, |
| base::OnceClosure task) { |
| gl_thread_->task_runner()->PostTask(from_here, std::move(task)); |
| } |
| |
| void VrShell::Navigate(GURL url, NavigationMethod method) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| |
| // Record metrics. |
| if (method == NavigationMethod::kOmniboxSuggestionSelected || |
| method == NavigationMethod::kOmniboxUrlEntry) { |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) |
| metrics_helper->RecordUrlRequested(url, method); |
| } |
| |
| Java_VrShell_loadUrl(env, j_vr_shell_, |
| base::android::ConvertUTF8ToJavaString(env, url.spec())); |
| } |
| |
| void VrShell::NavigateBack() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_navigateBack(env, j_vr_shell_); |
| } |
| |
| void VrShell::NavigateForward() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_navigateForward(env, j_vr_shell_); |
| } |
| |
| void VrShell::ReloadTab() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_reloadTab(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenNewTab(bool incognito) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openNewTab(env, j_vr_shell_, incognito); |
| } |
| |
| void VrShell::SelectTab(int id, bool incognito) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_selectTab(env, j_vr_shell_, id, incognito); |
| } |
| |
| void VrShell::OpenBookmarks() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openBookmarks(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenRecentTabs() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openRecentTabs(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenHistory() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openHistory(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenDownloads() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openDownloads(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenShare() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openShare(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenSettings() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openSettings(env, j_vr_shell_); |
| } |
| |
| void VrShell::CloseTab(int id, bool incognito) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_closeTab(env, j_vr_shell_, id, incognito); |
| } |
| |
| void VrShell::CloseAllTabs() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_closeAllTabs(env, j_vr_shell_); |
| } |
| |
| void VrShell::CloseAllIncognitoTabs() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_closeAllIncognitoTabs(env, j_vr_shell_); |
| } |
| |
| void VrShell::OpenFeedback() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_openFeedback(env, j_vr_shell_); |
| } |
| |
| void VrShell::CloseHostedDialog() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_closeCurrentDialog(env, j_vr_shell_); |
| } |
| |
| void VrShell::ToggleCardboardGamepad(bool enabled) { |
| // Enable/disable updating gamepad state. |
| if (cardboard_gamepad_source_active_ && !enabled) { |
| device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory( |
| device::GAMEPAD_SOURCE_CARDBOARD); |
| cardboard_gamepad_data_fetcher_ = nullptr; |
| cardboard_gamepad_source_active_ = false; |
| } |
| |
| if (!cardboard_gamepad_source_active_ && enabled) { |
| device::GvrDevice* device = delegate_provider_->GetDevice(); |
| if (!device) |
| return; |
| |
| device::GamepadDataFetcherManager::GetInstance()->AddFactory( |
| new device::CardboardGamepadDataFetcher::Factory(this, |
| device->GetId())); |
| cardboard_gamepad_source_active_ = true; |
| if (pending_cardboard_trigger_) { |
| OnTriggerEvent(nullptr, JavaParamRef<jobject>(nullptr), true); |
| } |
| pending_cardboard_trigger_ = false; |
| } |
| } |
| |
| void VrShell::ToggleGvrGamepad(bool enabled) { |
| // Enable/disable updating gamepad state. |
| if (enabled) { |
| DCHECK(!gvr_gamepad_source_active_); |
| device::GvrDevice* device = delegate_provider_->GetDevice(); |
| if (!device) |
| return; |
| |
| device::GamepadDataFetcherManager::GetInstance()->AddFactory( |
| new device::GvrGamepadDataFetcher::Factory(this, device->GetId())); |
| gvr_gamepad_source_active_ = true; |
| } else { |
| DCHECK(gvr_gamepad_source_active_); |
| device::GamepadDataFetcherManager::GetInstance()->RemoveSourceFactory( |
| device::GAMEPAD_SOURCE_GVR); |
| gvr_gamepad_data_fetcher_ = nullptr; |
| gvr_gamepad_source_active_ = false; |
| } |
| } |
| |
| void VrShell::OnTriggerEvent(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| bool touched) { |
| // If we are running cardboard, update gamepad state. |
| if (cardboard_gamepad_source_active_) { |
| device::CardboardGamepadData pad; |
| pad.timestamp = cardboard_gamepad_timer_++; |
| pad.is_screen_touching = touched; |
| if (cardboard_gamepad_data_fetcher_) { |
| cardboard_gamepad_data_fetcher_->SetGamepadData(pad); |
| } |
| } else { |
| pending_cardboard_trigger_ = touched; |
| } |
| |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::OnTriggerEvent, |
| gl_thread_->GetBrowserRenderer(), touched)); |
| } |
| |
| void VrShell::OnPause(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| PostToGlThread(FROM_HERE, base::BindOnce(&BrowserRenderer::OnPause, |
| gl_thread_->GetBrowserRenderer())); |
| |
| // exit vr session |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) |
| metrics_helper->SetVRActive(false); |
| SetIsInVR(GetNonNativePageWebContents(), false); |
| |
| poll_capturing_state_task_.Cancel(); |
| } |
| |
| void VrShell::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| // Calling WaitForAssets before BrowserRenderer::OnResume so that the UI won't |
| // accidentally produce an initial frame with UI. |
| if (can_load_new_assets_) { |
| ui_->WaitForAssets(); |
| LoadAssets(); |
| } |
| |
| PostToGlThread(FROM_HERE, base::BindOnce(&BrowserRenderer::OnResume, |
| gl_thread_->GetBrowserRenderer())); |
| |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) |
| metrics_helper->SetVRActive(true); |
| SetIsInVR(GetNonNativePageWebContents(), true); |
| |
| PollCapturingState(); |
| } |
| |
| void VrShell::SetSurface(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& surface) { |
| DCHECK(!reprojected_rendering_); |
| DCHECK(!surface.is_null()); |
| gfx::AcceleratedWidget window = |
| ANativeWindow_fromSurface(base::android::AttachCurrentThread(), surface); |
| surface_window_ = window; |
| gl_surface_created_event_.Signal(); |
| } |
| |
| void VrShell::SetWebVrMode(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| bool enabled) { |
| webvr_mode_ = enabled; |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) |
| metrics_helper->SetWebVREnabled(enabled); |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::SetWebXrMode, |
| gl_thread_->GetBrowserRenderer(), enabled)); |
| // We create and dispose a page info in order to get notifed of page |
| // permissions. |
| CreatePageInfo(); |
| ui_->SetWebVrMode(enabled); |
| |
| if (!webvr_mode_ && !web_vr_autopresentation_expected_) { |
| AssetsLoader::GetInstance()->GetMetricsHelper()->OnEnter(Mode::kVrBrowsing); |
| } else { |
| AssetsLoader::GetInstance()->GetMetricsHelper()->OnEnter( |
| Mode::kWebXrVrPresentation); |
| } |
| } |
| |
| bool VrShell::GetWebVrMode(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
| return webvr_mode_; |
| } |
| |
| bool VrShell::IsDisplayingUrlForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| return ShouldDisplayURL(); |
| } |
| |
| base::android::ScopedJavaLocalRef<jobject> |
| VrShell::GetVrInputConnectionForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| if (!vr_input_connection_) |
| return nullptr; |
| |
| return vr_input_connection_->GetJavaObject(); |
| } |
| |
| void VrShell::OnLoadProgressChanged(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| double progress) { |
| ui_->SetLoadProgress(progress); |
| } |
| |
| void VrShell::OnTabListCreated(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jobjectArray tabs, |
| jobjectArray incognito_tabs) { |
| ui_->RemoveAllTabs(); |
| size_t len = env->GetArrayLength(incognito_tabs); |
| for (size_t i = 0; i < len; ++i) { |
| ScopedJavaLocalRef<jobject> j_tab( |
| env, env->GetObjectArrayElement(incognito_tabs, i)); |
| TabAndroid* tab = TabAndroid::GetNativeTab(env, j_tab); |
| ui_->AddOrUpdateTab(tab->GetAndroidId(), true, tab->GetTitle()); |
| } |
| |
| len = env->GetArrayLength(tabs); |
| for (size_t i = 0; i < len; ++i) { |
| ScopedJavaLocalRef<jobject> j_tab(env, env->GetObjectArrayElement(tabs, i)); |
| TabAndroid* tab = TabAndroid::GetNativeTab(env, j_tab); |
| ui_->AddOrUpdateTab(tab->GetAndroidId(), false, tab->GetTitle()); |
| } |
| } |
| |
| void VrShell::OnTabUpdated(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean incognito, |
| jint id, |
| jstring jtitle) { |
| base::string16 title; |
| base::android::ConvertJavaStringToUTF16(env, jtitle, &title); |
| ui_->AddOrUpdateTab(id, incognito, title); |
| } |
| |
| void VrShell::OnTabRemoved(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean incognito, |
| jint id) { |
| ui_->RemoveTab(id, incognito); |
| } |
| |
| void VrShell::SetAlertDialog(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| float width, |
| float height) { |
| PostToGlThread(FROM_HERE, base::BindOnce(&BrowserRenderer::EnableAlertDialog, |
| gl_thread_->GetBrowserRenderer(), |
| gl_thread_.get(), width, height)); |
| } |
| |
| void VrShell::CloseAlertDialog( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| PostToGlThread(FROM_HERE, base::BindOnce(&BrowserRenderer::DisableAlertDialog, |
| gl_thread_->GetBrowserRenderer())); |
| // This will refresh our permissions after an alert is closed which should |
| // ensure that long press on the app button gives accurate results. |
| CreatePageInfo(); |
| } |
| |
| void VrShell::SetDialogBufferSize( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| int width, |
| int height) { |
| if (ui_surface_texture_) |
| ui_surface_texture_->SetDefaultBufferSize(width, height); |
| } |
| |
| void VrShell::SetAlertDialogSize( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| float width, |
| float height) { |
| PostToGlThread(FROM_HERE, base::BindOnce(&BrowserRenderer::SetAlertDialogSize, |
| gl_thread_->GetBrowserRenderer(), |
| width, height)); |
| } |
| |
| void VrShell::SetDialogLocation(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| float x, |
| float y) { |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::SetDialogLocation, |
| gl_thread_->GetBrowserRenderer(), x, y)); |
| } |
| |
| void VrShell::SetDialogFloating(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| bool floating) { |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::SetDialogFloating, |
| gl_thread_->GetBrowserRenderer(), floating)); |
| } |
| |
| void VrShell::ShowToast(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jstring jtext) { |
| base::string16 text; |
| base::android::ConvertJavaStringToUTF16(env, jtext, &text); |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::ShowToast, |
| gl_thread_->GetBrowserRenderer(), text)); |
| } |
| |
| void VrShell::CancelToast(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| PostToGlThread(FROM_HERE, base::BindOnce(&BrowserRenderer::CancelToast, |
| gl_thread_->GetBrowserRenderer())); |
| } |
| |
| void VrShell::ConnectPresentingService( |
| device::mojom::VRDisplayInfoPtr display_info, |
| device::mojom::XRRuntimeSessionOptionsPtr options) { |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::ConnectPresentingService, |
| gl_thread_->GetBrowserRenderer(), |
| std::move(display_info), std::move(options))); |
| } |
| |
| void VrShell::SetHistoryButtonsEnabled(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| jboolean can_go_back, |
| jboolean can_go_forward) { |
| ui_->SetHistoryButtonsEnabled(can_go_back, can_go_forward); |
| } |
| |
| void VrShell::RequestToExitVr(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| int reason) { |
| ui_->ShowExitVrPrompt(static_cast<UiUnsupportedMode>(reason)); |
| } |
| |
| void VrShell::ContentSurfaceCreated(jobject surface, |
| gl::SurfaceTexture* texture) { |
| content_surface_texture_ = texture; |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| base::android::ScopedJavaGlobalRef<jobject> ref(env, surface); |
| Java_VrShell_contentSurfaceCreated(env, j_vr_shell_, ref); |
| } |
| |
| void VrShell::ContentOverlaySurfaceCreated(jobject surface, |
| gl::SurfaceTexture* texture) { |
| overlay_surface_texture_ = texture; |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| base::android::ScopedJavaGlobalRef<jobject> ref(env, surface); |
| Java_VrShell_contentOverlaySurfaceCreated(env, j_vr_shell_, ref); |
| } |
| |
| void VrShell::DialogSurfaceCreated(jobject surface, |
| gl::SurfaceTexture* texture) { |
| ui_surface_texture_ = texture; |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| base::android::ScopedJavaGlobalRef<jobject> ref(env, surface); |
| Java_VrShell_dialogSurfaceCreated(env, j_vr_shell_, ref); |
| } |
| |
| void VrShell::GvrDelegateReady(gvr::ViewerType viewer_type) { |
| delegate_provider_->SetDelegate(this, viewer_type); |
| } |
| |
| void VrShell::SendRequestPresentReply(device::mojom::XRSessionPtr session) { |
| delegate_provider_->SendRequestPresentReply(std::move(session)); |
| } |
| |
| void VrShell::BufferBoundsChanged(JNIEnv* env, |
| const JavaParamRef<jobject>& object, |
| jint content_width, |
| jint content_height, |
| jint overlay_width, |
| jint overlay_height) { |
| TRACE_EVENT0("gpu", "VrShell::ContentPhysicalBoundsChanged"); |
| if (content_surface_texture_) { |
| content_surface_texture_->SetDefaultBufferSize(content_width, |
| content_height); |
| } |
| if (overlay_surface_texture_) { |
| overlay_surface_texture_->SetDefaultBufferSize(overlay_width, |
| overlay_height); |
| } |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::BufferBoundsChanged, |
| gl_thread_->GetBrowserRenderer(), |
| gfx::Size(content_width, content_height), |
| gfx::Size(overlay_width, overlay_height))); |
| } |
| |
| void VrShell::ResumeContentRendering(JNIEnv* env, |
| const JavaParamRef<jobject>& object) { |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::ResumeContentRendering, |
| gl_thread_->GetBrowserRenderer())); |
| } |
| |
| void VrShell::OnOverlayTextureEmptyChanged(JNIEnv* env, |
| const JavaParamRef<jobject>& object, |
| jboolean empty) { |
| ui_->SetOverlayTextureEmpty(empty); |
| } |
| |
| void VrShell::ContentWebContentsDestroyed() { |
| web_contents_ = nullptr; |
| } |
| |
| void VrShell::ForceExitVr() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_forceExitVr(env, j_vr_shell_); |
| } |
| |
| void VrShell::ExitPresent() { |
| delegate_provider_->ExitWebVRPresent(); |
| } |
| |
| void VrShell::ExitFullscreen() { |
| if (GetNonNativePageWebContents() && |
| GetNonNativePageWebContents()->IsFullscreen()) { |
| GetNonNativePageWebContents()->ExitFullscreen(false); |
| } |
| } |
| |
| void VrShell::LogUnsupportedModeUserMetric(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| int mode) { |
| LogUnsupportedModeUserMetric((UiUnsupportedMode)mode); |
| } |
| |
| void VrShell::RecordVrStartAction(VrStartAction action) { |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) { |
| metrics_helper->RecordVrStartAction(action); |
| } |
| } |
| |
| void VrShell::RecordPresentationStartAction(PresentationStartAction action) { |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) |
| metrics_helper->RecordPresentationStartAction(action); |
| } |
| |
| void VrShell::ShowSoftInput(JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| bool show) { |
| ui_->ShowSoftInput(show); |
| } |
| |
| void VrShell::UpdateWebInputIndices( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| int selection_start, |
| int selection_end, |
| int composition_start, |
| int composition_end) { |
| PostToGlThread(FROM_HERE, base::BindOnce(&VrGLThread::UpdateWebInputIndices, |
| base::Unretained(gl_thread_.get()), |
| selection_start, selection_end, |
| composition_start, composition_end)); |
| } |
| |
| void VrShell::LogUnsupportedModeUserMetric(UiUnsupportedMode mode) { |
| UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode, |
| UiUnsupportedMode::kCount); |
| } |
| |
| content::WebContents* VrShell::GetNonNativePageWebContents() const { |
| return !web_contents_is_native_page_ ? web_contents_ : nullptr; |
| } |
| |
| void VrShell::OnUnsupportedMode(UiUnsupportedMode mode) { |
| switch (mode) { |
| case UiUnsupportedMode::kUnhandledCodePoint: |
| // We should never have this case. |
| CHECK(false); |
| return; |
| case UiUnsupportedMode::kVoiceSearchNeedsRecordAudioOsPermission: { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_onUnhandledPermissionPrompt(env, j_vr_shell_); |
| return; |
| } |
| case UiUnsupportedMode::kNeedsKeyboardUpdate: { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_onNeedsKeyboardUpdate(env, j_vr_shell_); |
| return; |
| } |
| // These modes are not sent by the UI anymore. Enum values still exist to |
| // show correct exit prompt if vr-browsing-native-android-ui flag is false. |
| case UiUnsupportedMode::kUnhandledPageInfo: |
| case UiUnsupportedMode::kUnhandledCertificateInfo: |
| case UiUnsupportedMode::kUnhandledConnectionSecurityInfo: |
| case UiUnsupportedMode::kGenericUnsupportedFeature: |
| // kSearchEnginePromo should directly DOFF without showing a promo. So it |
| // should never be used from VR ui thread. |
| case UiUnsupportedMode::kSearchEnginePromo: |
| case UiUnsupportedMode::kCount: |
| NOTREACHED(); |
| return; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| void VrShell::OnExitVrPromptResult(UiUnsupportedMode reason, |
| ExitVrPromptChoice choice) { |
| bool should_exit; |
| switch (choice) { |
| case ExitVrPromptChoice::CHOICE_NONE: |
| case ExitVrPromptChoice::CHOICE_STAY: |
| should_exit = false; |
| break; |
| case ExitVrPromptChoice::CHOICE_EXIT: |
| should_exit = true; |
| break; |
| } |
| |
| DCHECK_NE(reason, UiUnsupportedMode::kCount); |
| if (reason == UiUnsupportedMode::kVoiceSearchNeedsRecordAudioOsPermission) { |
| // Note that we already measure the number of times the user exits VR |
| // because of the record audio permission through |
| // VR.Shell.EncounteredUnsupportedMode histogram. This histogram measures |
| // whether the user chose to proceed to grant the OS record audio permission |
| // through the reported Boolean. |
| UMA_HISTOGRAM_BOOLEAN("VR.VoiceSearch.RecordAudioOsPermissionPromptChoice", |
| choice == ExitVrPromptChoice::CHOICE_EXIT); |
| } |
| |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_onExitVrRequestResult(env, j_vr_shell_, static_cast<int>(reason), |
| should_exit); |
| } |
| |
| void VrShell::OnContentScreenBoundsChanged(const gfx::SizeF& bounds) { |
| // We have to fit the content into the portion of the display for one eye. |
| // Thus, take only half the width. |
| int width_pixels = display_size_pixels_.width() / 2; |
| float width_meters = display_size_meters_.width() / 2; |
| |
| // The physical and pixel dimensions to draw the scene for one eye needs to be |
| // a square so that the content's aspect ratio is preserved (i.e. make pixels |
| // square for the calculations). |
| |
| // For the resolution err on the side of too many pixels so that our content |
| // is rather drawn with too high of a resolution than too low. |
| int length_pixels = std::max(width_pixels, display_size_pixels_.height()); |
| |
| // For the size err on the side of a too small area so that the font size is |
| // rather too big than too small. |
| float length_meters = std::min(width_meters, display_size_meters_.height()); |
| |
| // Calculate the virtual window size and DPR and pass this to VrShell. |
| gfx::Size window_size = gfx::ToRoundedSize( |
| gfx::ScaleSize(bounds, width_meters * kContentBoundsMetersToWindowSize)); |
| // Need to use sqrt(kContentDprFactor) to translate from a factor applicable |
| // to the area to a factor applicable to one side length. |
| float dpr = |
| (length_pixels / (length_meters * kContentBoundsMetersToWindowSize)) * |
| std::sqrt(kContentDprFactor); |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_setContentCssSize(env, j_vr_shell_, window_size.width(), |
| window_size.height(), dpr); |
| |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::ContentBoundsChanged, |
| gl_thread_->GetBrowserRenderer(), |
| window_size.width(), window_size.height())); |
| } |
| |
| void VrShell::SetVoiceSearchActive(bool active) { |
| if (!active && !speech_recognizer_) |
| return; |
| |
| if (!HasAudioPermission()) { |
| OnUnsupportedMode( |
| UiUnsupportedMode::kVoiceSearchNeedsRecordAudioOsPermission); |
| return; |
| } |
| |
| if (!speech_recognizer_) { |
| Profile* profile = ProfileManager::GetActiveUserProfile(); |
| std::string profile_locale = g_browser_process->GetApplicationLocale(); |
| speech_recognizer_.reset(new SpeechRecognizer( |
| this, ui_, |
| content::BrowserContext::GetDefaultStoragePartition(profile) |
| ->GetURLLoaderFactoryForBrowserProcessIOThread(), |
| profile->GetPrefs()->GetString(prefs::kAcceptLanguages), |
| profile_locale)); |
| } |
| if (active) { |
| speech_recognizer_->Start(); |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper) |
| metrics_helper->RecordVoiceSearchStarted(); |
| } else { |
| speech_recognizer_->Stop(); |
| } |
| } |
| |
| void VrShell::StartAutocomplete(const AutocompleteRequest& request) { |
| autocomplete_controller_->Start(request); |
| } |
| |
| void VrShell::StopAutocomplete() { |
| autocomplete_controller_->Stop(); |
| } |
| |
| void VrShell::ShowPageInfo() { |
| Java_VrShell_showPageInfo(base::android::AttachCurrentThread(), j_vr_shell_); |
| } |
| |
| bool VrShell::HasAudioPermission() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| return Java_VrShell_hasAudioPermission(env, j_vr_shell_); |
| } |
| |
| void VrShell::PollCapturingState() { |
| poll_capturing_state_task_.Reset(base::BindRepeating( |
| &VrShell::PollCapturingState, base::Unretained(this))); |
| main_thread_task_runner_->PostDelayedTask( |
| FROM_HERE, poll_capturing_state_task_.callback(), |
| kPollCapturingStateInterval); |
| |
| scoped_refptr<MediaStreamCaptureIndicator> indicator = |
| MediaCaptureDevicesDispatcher::GetInstance() |
| ->GetMediaStreamCaptureIndicator(); |
| |
| active_capturing_.audio_capture_enabled = false; |
| active_capturing_.video_capture_enabled = false; |
| active_capturing_.screen_capture_enabled = false; |
| active_capturing_.bluetooth_connected = false; |
| background_capturing_.audio_capture_enabled = false; |
| background_capturing_.video_capture_enabled = false; |
| background_capturing_.screen_capture_enabled = false; |
| background_capturing_.bluetooth_connected = false; |
| |
| std::unique_ptr<content::RenderWidgetHostIterator> widgets( |
| content::RenderWidgetHost::GetRenderWidgetHosts()); |
| while (content::RenderWidgetHost* rwh = widgets->GetNextHost()) { |
| bool is_foreground = rwh->GetProcess()->VisibleClientCount() > 0; |
| content::RenderViewHost* rvh = content::RenderViewHost::From(rwh); |
| if (!rvh) |
| continue; |
| content::WebContents* web_contents = |
| content::WebContents::FromRenderViewHost(rvh); |
| if (!web_contents) |
| continue; |
| if (web_contents->GetRenderViewHost() != rvh) |
| continue; |
| |
| // Because a WebContents can only have one current RVH at a time, there will |
| // be no duplicate WebContents here. |
| if (indicator->IsCapturingAudio(web_contents)) { |
| if (is_foreground) |
| active_capturing_.audio_capture_enabled = true; |
| else |
| background_capturing_.audio_capture_enabled = true; |
| } |
| if (indicator->IsCapturingVideo(web_contents)) { |
| if (is_foreground) |
| active_capturing_.video_capture_enabled = true; |
| else |
| background_capturing_.video_capture_enabled = true; |
| } |
| if (indicator->IsBeingMirrored(web_contents)) { |
| if (is_foreground) |
| active_capturing_.screen_capture_enabled = true; |
| else |
| background_capturing_.screen_capture_enabled = true; |
| } |
| if (web_contents->IsConnectedToBluetoothDevice()) { |
| if (is_foreground) |
| active_capturing_.bluetooth_connected = true; |
| else |
| background_capturing_.bluetooth_connected = true; |
| } |
| } |
| |
| geolocation_config_->IsHighAccuracyLocationBeingCaptured(base::BindRepeating( |
| [](VrShell* shell, BrowserUiInterface* ui, |
| CapturingStateModel* active_capturing, |
| CapturingStateModel* background_capturing, |
| CapturingStateModel* potential_capturing, |
| bool high_accuracy_location) { |
| active_capturing->location_access_enabled = high_accuracy_location; |
| ui->SetCapturingState(*active_capturing, *background_capturing, |
| *potential_capturing); |
| }, |
| base::Unretained(this), base::Unretained(ui_), |
| base::Unretained(&active_capturing_), |
| base::Unretained(&background_capturing_), |
| base::Unretained(&potential_capturing_))); |
| } |
| |
| void VrShell::ClearFocusedElement() { |
| if (!web_contents_) |
| return; |
| |
| web_contents_->ClearFocusedElement(); |
| } |
| |
| void VrShell::ProcessContentGesture(std::unique_ptr<InputEvent> event, |
| int content_id) { |
| // Block the events if they don't belong to the current content |
| if (content_id_ != content_id) |
| return; |
| |
| if (!android_ui_gesture_target_) |
| return; |
| |
| android_ui_gesture_target_->DispatchInputEvent(std::move(event)); |
| } |
| |
| void VrShell::ProcessDialogGesture(std::unique_ptr<InputEvent> event) { |
| if (!dialog_gesture_target_) |
| return; |
| |
| dialog_gesture_target_->DispatchInputEvent(std::move(event)); |
| } |
| |
| void VrShell::UpdateGamepadData(device::GvrGamepadData pad) { |
| if (gvr_gamepad_source_active_ != pad.connected) |
| ToggleGvrGamepad(pad.connected); |
| |
| if (gvr_gamepad_data_fetcher_) |
| gvr_gamepad_data_fetcher_->SetGamepadData(pad); |
| } |
| |
| void VrShell::RegisterGvrGamepadDataFetcher( |
| device::GvrGamepadDataFetcher* fetcher) { |
| DVLOG(1) << __FUNCTION__ << "(" << fetcher << ")"; |
| gvr_gamepad_data_fetcher_ = fetcher; |
| } |
| |
| void VrShell::RegisterCardboardGamepadDataFetcher( |
| device::CardboardGamepadDataFetcher* fetcher) { |
| DVLOG(1) << __FUNCTION__ << "(" << fetcher << ")"; |
| cardboard_gamepad_data_fetcher_ = fetcher; |
| } |
| |
| bool VrShell::HasDaydreamSupport(JNIEnv* env) { |
| return Java_VrShell_hasDaydreamSupport(env, j_vr_shell_); |
| } |
| |
| content::WebContents* VrShell::GetActiveWebContents() const { |
| // TODO(tiborg): Handle the case when Tab#isShowingErrorPage returns true. |
| return web_contents_; |
| } |
| |
| bool VrShell::ShouldDisplayURL() const { |
| content::NavigationEntry* entry = GetNavigationEntry(); |
| if (!entry) { |
| return ChromeToolbarModelDelegate::ShouldDisplayURL(); |
| } |
| GURL url = entry->GetVirtualURL(); |
| // URL is of the form chrome-native://.... This is not useful for the user. |
| // Hide it. |
| if (url.SchemeIs(chrome::kChromeUINativeScheme)) { |
| return false; |
| } |
| // URL is of the form chrome://.... |
| if (url.SchemeIs(content::kChromeUIScheme)) { |
| return true; |
| } |
| return ChromeToolbarModelDelegate::ShouldDisplayURL(); |
| } |
| |
| void VrShell::OnVoiceResults(const base::string16& result) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| GURL url; |
| bool input_was_url; |
| std::tie(url, input_was_url) = |
| autocomplete_controller_->GetUrlFromVoiceInput(result); |
| |
| // TODO(http://crbug.com/817559): If the user is doing a voice search from the |
| // new tab page, no metrics data is recorded (including voice search started). |
| // Fix this. |
| |
| // This should happen before the load to avoid concurency issues. |
| SessionMetricsHelper* metrics_helper = |
| SessionMetricsHelper::FromWebContents(web_contents_); |
| if (metrics_helper && input_was_url) |
| metrics_helper->RecordUrlRequested(url, NavigationMethod::kVoiceSearch); |
| |
| Java_VrShell_loadUrl(env, j_vr_shell_, |
| base::android::ConvertUTF8ToJavaString(env, url.spec())); |
| } |
| |
| void VrShell::OnAssetsLoaded(AssetsLoadStatus status, |
| std::unique_ptr<Assets> assets, |
| const base::Version& component_version) { |
| ui_->OnAssetsLoaded(status, std::move(assets), component_version); |
| |
| if (status == AssetsLoadStatus::kSuccess) { |
| VLOG(1) << "Successfully loaded VR assets component"; |
| } else { |
| VLOG(1) << "Failed to load VR assets component"; |
| } |
| |
| AssetsLoader::GetInstance()->GetMetricsHelper()->OnAssetsLoaded( |
| status, component_version); |
| ui_finished_loading_ = true; |
| } |
| |
| void VrShell::LoadAssets() { |
| can_load_new_assets_ = false; |
| AssetsLoader::GetInstance()->Load( |
| base::BindOnce(&VrShell::OnAssetsLoaded, base::Unretained(this))); |
| } |
| |
| void VrShell::OnAssetsComponentReady() { |
| can_load_new_assets_ = true; |
| // We don't apply updates after the timer expires because that would lead to |
| // replacing the user's environment. New updates will be applied when |
| // re-entering VR. |
| if (waiting_for_assets_component_timer_.IsRunning()) { |
| waiting_for_assets_component_timer_.Stop(); |
| LoadAssets(); |
| } |
| } |
| |
| void VrShell::OnAssetsComponentWaitTimeout() { |
| ui_->OnAssetsUnavailable(); |
| ui_finished_loading_ = true; |
| } |
| |
| void VrShell::SetCookieInfo(const CookieInfoList& cookie_info_list) {} |
| |
| void VrShell::SetPermissionInfo(const PermissionInfoList& permission_info_list, |
| ChosenObjectInfoList chosen_object_info_list) { |
| // Here we'll check the current web contents for potentially in-use |
| // permissions. Accepting bluetooth is immersive mode is not currently |
| // supported, so we will not check here. Also, the ability to cast is not a |
| // page-specific potentiality, so we will not check for this, either. |
| for (const auto& info : permission_info_list) { |
| switch (info.type) { |
| case CONTENT_SETTINGS_TYPE_GEOLOCATION: |
| potential_capturing_.location_access_enabled = |
| info.setting == CONTENT_SETTING_ALLOW; |
| break; |
| case CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC: |
| potential_capturing_.audio_capture_enabled = |
| info.setting == CONTENT_SETTING_ALLOW; |
| break; |
| case CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA: |
| potential_capturing_.video_capture_enabled = |
| info.setting == CONTENT_SETTING_ALLOW; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| void VrShell::SetIdentityInfo(const IdentityInfo& identity_info) {} |
| |
| void VrShell::AcceptDoffPromptForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj) { |
| PostToGlThread(FROM_HERE, |
| base::BindOnce(&BrowserRenderer::AcceptDoffPromptForTesting, |
| gl_thread_->GetBrowserRenderer())); |
| } |
| |
| void VrShell::SetUiExpectingActivityForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jint quiescence_timeout_ms) { |
| UiTestActivityExpectation ui_expectation; |
| ui_expectation.quiescence_timeout_ms = quiescence_timeout_ms; |
| PostToGlThread( |
| FROM_HERE, |
| base::BindOnce(&BrowserRenderer::SetUiExpectingActivityForTesting, |
| gl_thread_->GetBrowserRenderer(), ui_expectation)); |
| } |
| |
| void VrShell::SaveNextFrameBufferToDiskForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jstring filepath_base) { |
| PostToGlThread( |
| FROM_HERE, |
| base::BindOnce( |
| &BrowserRenderer::SaveNextFrameBufferToDiskForTesting, |
| gl_thread_->GetBrowserRenderer(), |
| base::android::ConvertJavaStringToUTF8(env, filepath_base))); |
| } |
| |
| void VrShell::WatchElementForVisibilityChangeForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jint element_name, |
| jint timeout_ms) { |
| VisibilityChangeExpectation visibility_expectation; |
| visibility_expectation.element_name = |
| static_cast<UserFriendlyElementName>(element_name); |
| visibility_expectation.timeout_ms = timeout_ms; |
| PostToGlThread( |
| FROM_HERE, |
| base::BindOnce( |
| &BrowserRenderer::WatchElementForVisibilityChangeForTesting, |
| gl_thread_->GetBrowserRenderer(), visibility_expectation)); |
| } |
| |
| void VrShell::ReportUiOperationResultForTesting(UiTestOperationType action_type, |
| UiTestOperationResult result) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_VrShell_reportUiOperationResultForTesting(env, j_vr_shell_, |
| static_cast<int>(action_type), |
| static_cast<int>(result)); |
| } |
| |
| void VrShell::PerformControllerActionForTesting( |
| JNIEnv* env, |
| const base::android::JavaParamRef<jobject>& obj, |
| jint element_name, |
| jint action_type, |
| jfloat x, |
| jfloat y) { |
| ControllerTestInput controller_input; |
| controller_input.element_name = |
| static_cast<UserFriendlyElementName>(element_name); |
| controller_input.action = static_cast<VrControllerTestAction>(action_type); |
| controller_input.position = gfx::PointF(x, y); |
| PostToGlThread( |
| FROM_HERE, |
| base::BindOnce(&BrowserRenderer::PerformControllerActionForTesting, |
| gl_thread_->GetBrowserRenderer(), controller_input)); |
| } |
| |
| std::unique_ptr<PageInfo> VrShell::CreatePageInfo() { |
| if (!web_contents_) |
| return nullptr; |
| |
| content::NavigationEntry* entry = |
| web_contents_->GetController().GetVisibleEntry(); |
| if (!entry) |
| return nullptr; |
| |
| SecurityStateTabHelper* helper = |
| SecurityStateTabHelper::FromWebContents(web_contents_); |
| security_state::SecurityInfo security_info; |
| helper->GetSecurityInfo(&security_info); |
| |
| return std::make_unique<PageInfo>( |
| this, Profile::FromBrowserContext(web_contents_->GetBrowserContext()), |
| TabSpecificContentSettings::FromWebContents(web_contents_), web_contents_, |
| entry->GetVirtualURL(), security_info); |
| } |
| |
| gfx::AcceleratedWidget VrShell::GetRenderSurface() { |
| return surface_window_; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Native JNI methods |
| // ---------------------------------------------------------------------------- |
| |
| jlong JNI_VrShell_Init(JNIEnv* env, |
| const JavaParamRef<jobject>& obj, |
| const JavaParamRef<jobject>& delegate, |
| jboolean for_web_vr, |
| jboolean browsing_disabled, |
| jboolean has_or_can_request_audio_permission, |
| jlong gvr_api, |
| jboolean reprojected_rendering, |
| jfloat display_width_meters, |
| jfloat display_height_meters, |
| jint display_width_pixels, |
| jint display_pixel_height, |
| jboolean pause_content, |
| jboolean low_density, |
| jboolean is_standalone_vr_device) { |
| UiInitialState ui_initial_state; |
| ui_initial_state.browsing_disabled = browsing_disabled; |
| ui_initial_state.in_web_vr = for_web_vr; |
| ui_initial_state.has_or_can_request_audio_permission = |
| has_or_can_request_audio_permission; |
| ui_initial_state.assets_supported = AssetsLoader::AssetsSupported(); |
| ui_initial_state.is_standalone_vr_device = is_standalone_vr_device; |
| ui_initial_state.create_tabs_view = |
| base::FeatureList::IsEnabled(chrome::android::kVrBrowsingTabsView); |
| ui_initial_state.use_new_incognito_strings = |
| base::FeatureList::IsEnabled(features::kIncognitoStrings); |
| |
| return reinterpret_cast<intptr_t>(new VrShell( |
| env, obj, ui_initial_state, |
| VrShellDelegate::GetNativeVrShellDelegate(env, delegate), |
| reinterpret_cast<gvr_context*>(gvr_api), reprojected_rendering, |
| display_width_meters, display_height_meters, display_width_pixels, |
| display_pixel_height, pause_content, low_density)); |
| } |
| |
| } // namespace vr |