From 22d1cccafeb4f4894bfc356dc5fd044711e3b005 Mon Sep 17 00:00:00 2001 From: Simon Hong Date: Wed, 2 Aug 2023 12:47:57 +0900 Subject: [PATCH] Informs to users about system location service fix https://github.com/brave/brave-browser/issues/16897 If system location service is enabled, browser can get more accurate geolocation data. Launch dialog about this when site asks geolocation permission if system location service is not yet enabled. --- app/brave_generated_resources.grd | 22 ++ browser/brave_profile_prefs.cc | 2 + browser/brave_tab_helpers.cc | 2 + ...geolocation_permission_context_delegate.cc | 27 ++- ..._geolocation_permission_context_delegate.h | 4 +- browser/ui/BUILD.gn | 13 + browser/ui/browser_dialogs.h | 4 + browser/ui/geolocation/BUILD.gn | 24 ++ .../geolocation_accuracy_browsertest.cc | 166 +++++++++++++ .../geolocation_accuracy_tab_helper.cc | 81 +++++++ .../geolocation_accuracy_tab_helper.h | 46 ++++ .../geolocation_accuracy_utils_win.cc | 68 ++++++ .../geolocation_accuracy_utils_win.h | 13 + browser/ui/geolocation/pref_names.h | 12 + ...geolocation_accuracy_helper_dialog_view.cc | 223 ++++++++++++++++++ .../geolocation_accuracy_helper_dialog_view.h | 47 ++++ test/BUILD.gn | 1 + 17 files changed, 750 insertions(+), 5 deletions(-) create mode 100644 browser/ui/geolocation/BUILD.gn create mode 100644 browser/ui/geolocation/geolocation_accuracy_browsertest.cc create mode 100644 browser/ui/geolocation/geolocation_accuracy_tab_helper.cc create mode 100644 browser/ui/geolocation/geolocation_accuracy_tab_helper.h create mode 100644 browser/ui/geolocation/geolocation_accuracy_utils_win.cc create mode 100644 browser/ui/geolocation/geolocation_accuracy_utils_win.h create mode 100644 browser/ui/geolocation/pref_names.h create mode 100644 browser/ui/views/geolocation_accuracy_helper_dialog_view.cc create mode 100644 browser/ui/views/geolocation_accuracy_helper_dialog_view.h diff --git a/app/brave_generated_resources.grd b/app/brave_generated_resources.grd index 4611b0c62613..6b0b622cece8 100644 --- a/app/brave_generated_resources.grd +++ b/app/brave_generated_resources.grd @@ -544,6 +544,28 @@ Or change later at $2brave://settings/ext this setting + + + Geolocation with Brave + + + The Brave browser is attempting to access your location. You may not get accurate results unless you enable your operating system's $1Location services setting and also enable $2Let apps access your location. + + + Location services + + + Let apps access your location + + + With those enabled, your location results should be much more accurate. $1Learn more + + + Learn more + + + Don't show this again + Close all diff --git a/browser/brave_profile_prefs.cc b/browser/brave_profile_prefs.cc index 433dac00322b..b1ef34a9b869 100644 --- a/browser/brave_profile_prefs.cc +++ b/browser/brave_profile_prefs.cc @@ -109,6 +109,7 @@ #if !BUILDFLAG(IS_ANDROID) #include "brave/browser/search_engines/search_engine_provider_util.h" +#include "brave/browser/ui/geolocation/pref_names.h" #include "brave/browser/ui/tabs/brave_tab_prefs.h" #include "brave/components/brave_private_new_tab_ui/common/pref_names.h" #endif @@ -439,6 +440,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { base::Value(true)); registry->RegisterBooleanPref(kEnableWindowClosingConfirm, true); registry->RegisterBooleanPref(kEnableClosingLastTab, true); + registry->RegisterBooleanPref(kShowGeolocationAccuracyHelperDialog, true); brave_tabs::RegisterBraveProfilePrefs(registry); #endif diff --git a/browser/brave_tab_helpers.cc b/browser/brave_tab_helpers.cc index 910e7d491510..5ae0a284435a 100644 --- a/browser/brave_tab_helpers.cc +++ b/browser/brave_tab_helpers.cc @@ -44,6 +44,7 @@ #if !BUILDFLAG(IS_ANDROID) #include "brave/browser/ui/brave_shields_data_controller.h" +#include "brave/browser/ui/geolocation/geolocation_accuracy_tab_helper.h" #include "chrome/browser/ui/thumbnails/thumbnail_tab_helper.h" #endif @@ -106,6 +107,7 @@ void AttachTabHelpers(content::WebContents* web_contents) { BraveBookmarkTabHelper::CreateForWebContents(web_contents); brave_shields::BraveShieldsDataController::CreateForWebContents(web_contents); ThumbnailTabHelper::CreateForWebContents(web_contents); + GeolocationAccuracyTabHelper::MaybeCreateForWebContents(web_contents); #endif brave_rewards::RewardsTabHelper::CreateForWebContents(web_contents); diff --git a/browser/geolocation/brave_geolocation_permission_context_delegate.cc b/browser/geolocation/brave_geolocation_permission_context_delegate.cc index 75dff77283b0..9ea029d10fc3 100644 --- a/browser/geolocation/brave_geolocation_permission_context_delegate.cc +++ b/browser/geolocation/brave_geolocation_permission_context_delegate.cc @@ -13,11 +13,15 @@ #include "content/public/browser/web_contents.h" #include "url/gurl.h" +#if !BUILDFLAG(IS_ANDROID) +#include "brave/browser/ui/geolocation/geolocation_accuracy_tab_helper.h" +#endif + BraveGeolocationPermissionContextDelegate:: BraveGeolocationPermissionContextDelegate( content::BrowserContext* browser_context) : GeolocationPermissionContextDelegate(browser_context), - profile_(Profile::FromBrowserContext(browser_context)) {} + profile_(*Profile::FromBrowserContext(browser_context)) {} BraveGeolocationPermissionContextDelegate:: ~BraveGeolocationPermissionContextDelegate() = default; @@ -33,6 +37,23 @@ bool BraveGeolocationPermissionContextDelegate::DecidePermission( return true; } - return GeolocationPermissionContextDelegate::DecidePermission( - id, requesting_origin, user_gesture, callback, context); + if (GeolocationPermissionContextDelegate::DecidePermission( + id, requesting_origin, user_gesture, callback, context)) { + return true; + } + +#if !BUILDFLAG(IS_ANDROID) + content::RenderFrameHost* rfh = + content::RenderFrameHost::FromID(id.global_render_frame_host_id()); + DCHECK(rfh); + + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(rfh); + if (GeolocationAccuracyTabHelper* accuracy_tab_helper = + GeolocationAccuracyTabHelper::FromWebContents(web_contents)) { + accuracy_tab_helper->LaunchAccuracyHelperDialogIfNeeded(); + } +#endif + + return false; } diff --git a/browser/geolocation/brave_geolocation_permission_context_delegate.h b/browser/geolocation/brave_geolocation_permission_context_delegate.h index d3ba9517dc19..7c569f838f7d 100644 --- a/browser/geolocation/brave_geolocation_permission_context_delegate.h +++ b/browser/geolocation/brave_geolocation_permission_context_delegate.h @@ -6,7 +6,7 @@ #ifndef BRAVE_BROWSER_GEOLOCATION_BRAVE_GEOLOCATION_PERMISSION_CONTEXT_DELEGATE_H_ #define BRAVE_BROWSER_GEOLOCATION_BRAVE_GEOLOCATION_PERMISSION_CONTEXT_DELEGATE_H_ -#include "base/memory/raw_ptr.h" +#include "base/memory/raw_ref.h" #include "chrome/browser/geolocation/geolocation_permission_context_delegate.h" class Profile; @@ -30,7 +30,7 @@ class BraveGeolocationPermissionContextDelegate permissions::GeolocationPermissionContext* context) override; private: - raw_ptr profile_ = nullptr; + const raw_ref profile_; }; #endif // BRAVE_BROWSER_GEOLOCATION_BRAVE_GEOLOCATION_PERMISSION_CONTEXT_DELEGATE_H_ diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index d0ddfece5c7a..9bf5d5e15ccd 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -97,6 +97,9 @@ source_set("ui") { "content_settings/brave_autoplay_content_setting_bubble_model.h", "content_settings/brave_content_setting_image_models.cc", "content_settings/brave_content_setting_image_models.h", + "geolocation/geolocation_accuracy_tab_helper.cc", + "geolocation/geolocation_accuracy_tab_helper.h", + "geolocation/pref_names.h", "omnibox/brave_omnibox_client_impl.cc", "omnibox/brave_omnibox_client_impl.h", "page_action/brave_page_action_icon_type.h", @@ -306,6 +309,8 @@ source_set("ui") { "views/frame/brave_opaque_browser_frame_view.h", "views/frame/brave_window_frame_graphic.cc", "views/frame/brave_window_frame_graphic.h", + "views/geolocation_accuracy_helper_dialog_view.cc", + "views/geolocation_accuracy_helper_dialog_view.h", "views/omnibox/brave_omnibox_popup_view_views.cc", "views/omnibox/brave_omnibox_popup_view_views.h", "views/omnibox/brave_omnibox_result_view.cc", @@ -427,6 +432,13 @@ source_set("ui") { ] } + if (is_win) { + sources += [ + "geolocation/geolocation_accuracy_utils_win.cc", + "geolocation/geolocation_accuracy_utils_win.h", + ] + } + # brave's obsolete infobar is only used on Windows and Mac now. if (is_win || is_mac) { sources += [ @@ -552,6 +564,7 @@ source_set("ui") { "//components/strings:components_strings", "//components/tab_groups", "//components/update_client", + "//components/user_prefs", "//content/public/browser", "//content/public/common", "//mojo/public/cpp/bindings", diff --git a/browser/ui/browser_dialogs.h b/browser/ui/browser_dialogs.h index fe8e313a1765..8168aeeb983e 100644 --- a/browser/ui/browser_dialogs.h +++ b/browser/ui/browser_dialogs.h @@ -25,6 +25,10 @@ void ShowCrashReportPermissionAskDialog(Browser* browser); // Run |callback| when dialog closed. void ShowObsoleteSystemConfirmDialog(base::OnceCallback callback); +// Launch dialog to inform that system location service is not enabled. +void ShowGeolocationAccuracyHelperDialog(content::WebContents* web_contents, + base::OnceClosure closing_callback); + #if BUILDFLAG(ENABLE_TEXT_RECOGNITION) // Show web modal dialog for showing text that recognized from |image|. void ShowTextRecognitionDialog(content::WebContents* web_contents, diff --git a/browser/ui/geolocation/BUILD.gn b/browser/ui/geolocation/BUILD.gn new file mode 100644 index 000000000000..fd59b8576a5d --- /dev/null +++ b/browser/ui/geolocation/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright (c) 2023 The Brave Authors. All rights reserved. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at https://mozilla.org/MPL/2.0/. + +source_set("browser_tests") { + testonly = true + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + + sources = [ "geolocation_accuracy_browsertest.cc" ] + + deps = [ + "//base", + "//brave/browser", + "//chrome/browser", + "//chrome/browser/ui", + "//chrome/test:test_support", + "//chrome/test:test_support_ui", + "//components/permissions", + "//components/permissions:test_support", + "//components/prefs", + "//content/test:test_support", + ] +} diff --git a/browser/ui/geolocation/geolocation_accuracy_browsertest.cc b/browser/ui/geolocation/geolocation_accuracy_browsertest.cc new file mode 100644 index 000000000000..2b046711cff5 --- /dev/null +++ b/browser/ui/geolocation/geolocation_accuracy_browsertest.cc @@ -0,0 +1,166 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include + +#include "base/functional/bind.h" +#include "base/run_loop.h" +#include "base/test/bind.h" +#include "brave/browser/ui/geolocation/geolocation_accuracy_tab_helper.h" +#include "brave/browser/ui/geolocation/pref_names.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/permissions/permission_request_manager.h" +#include "components/permissions/test/mock_permission_prompt_factory.h" +#include "components/prefs/pref_service.h" +#include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_mock_cert_verifier.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +class GeolocationAccuracyBrowserTest : public InProcessBrowserTest { + public: + GeolocationAccuracyBrowserTest() + : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} + + GeolocationAccuracyBrowserTest(const GeolocationAccuracyBrowserTest&) = + delete; + GeolocationAccuracyBrowserTest& operator=( + const GeolocationAccuracyBrowserTest&) = delete; + ~GeolocationAccuracyBrowserTest() override = default; + + void SetUpCommandLine(base::CommandLine* command_line) override { + InProcessBrowserTest::SetUpCommandLine(command_line); + mock_cert_verifier_.SetUpCommandLine(command_line); + } + + void SetUpInProcessBrowserTestFixture() override { + InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); + mock_cert_verifier_.SetUpInProcessBrowserTestFixture(); + } + + void TearDownInProcessBrowserTestFixture() override { + InProcessBrowserTest::TearDownInProcessBrowserTestFixture(); + mock_cert_verifier_.TearDownInProcessBrowserTestFixture(); + } + + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK); + permissions::PermissionRequestManager* manager = + permissions::PermissionRequestManager::FromWebContents( + GetActiveWebContents()); + mock_permission_prompt_factory_ = + std::make_unique(manager); + + host_resolver()->AddRule("*", "127.0.0.1"); + https_server()->ServeFilesFromSourceDirectory(GetChromeTestDataDir()); + ASSERT_TRUE(https_server()->Start()); + } + + void TearDownOnMainThread() override { + mock_permission_prompt_factory_.reset(); + } + + permissions::MockPermissionPromptFactory* prompt_factory() { + return mock_permission_prompt_factory_.get(); + } + + net::EmbeddedTestServer* https_server() { return &https_server_; } + base::RunLoop* run_loop() const { return run_loop_.get(); } + + void WaitUntil(base::RepeatingCallback condition) { + if (condition.Run()) { + return; + } + + base::RepeatingTimer scheduler; + scheduler.Start(FROM_HERE, base::Milliseconds(100), + base::BindLambdaForTesting([this, &condition]() { + if (condition.Run()) { + run_loop_->Quit(); + } + })); + Run(); + } + + void Run() { + run_loop_ = std::make_unique(); + run_loop()->Run(); + } + + content::WebContents* GetActiveWebContents() { + return browser()->tab_strip_model()->GetActiveWebContents(); + } + + content::RenderFrameHost* GetActiveMainFrame() { + return GetActiveWebContents()->GetPrimaryMainFrame(); + } + + void AcceptDialogForTesting() { + web_modal::WebContentsModalDialogManager* manager = + web_modal::WebContentsModalDialogManager::FromWebContents( + GetActiveWebContents()); + DCHECK(manager); + manager->CloseAllDialogs(); + } + + content::ContentMockCertVerifier mock_cert_verifier_; + net::test_server::EmbeddedTestServer https_server_; + std::unique_ptr + mock_permission_prompt_factory_; + std::unique_ptr run_loop_; +}; + +IN_PROC_BROWSER_TEST_F(GeolocationAccuracyBrowserTest, DialogLaunchTest) { + const GURL& url = https_server()->GetURL("/empty.html"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + + mock_permission_prompt_factory_->set_response_type( + permissions::PermissionRequestManager::AutoResponseType::DISMISS); + + // Check bubble is shown and dialog is also launched. + content::ExecuteScriptAsync( + GetActiveMainFrame(), + "navigator.geolocation.getCurrentPosition(function(){});"); + mock_permission_prompt_factory_->WaitForPermissionBubble(); + + GeolocationAccuracyTabHelper* accuracy_tab_helper = + GeolocationAccuracyTabHelper::FromWebContents(GetActiveWebContents()); + if (!accuracy_tab_helper) { + return; + } + + EXPECT_TRUE(accuracy_tab_helper->is_dialog_running_); + EXPECT_TRUE(accuracy_tab_helper->dialog_asked_in_current_navigation_); + + // Wait to dialog closing. + AcceptDialogForTesting(); + WaitUntil(base::BindLambdaForTesting( + [&]() { return !accuracy_tab_helper->is_dialog_running_; })); + EXPECT_TRUE(accuracy_tab_helper->dialog_asked_in_current_navigation_); + + // Navigate again and check flags are cleared. + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + EXPECT_FALSE(accuracy_tab_helper->is_dialog_running_); + EXPECT_FALSE(accuracy_tab_helper->dialog_asked_in_current_navigation_); + + // Disable dialog launching. + browser()->profile()->GetPrefs()->SetBoolean( + kShowGeolocationAccuracyHelperDialog, false); + + // Check bubble is shown again but dialog is not launched. + content::ExecuteScriptAsync( + GetActiveMainFrame(), + "navigator.geolocation.getCurrentPosition(function(){});"); + mock_permission_prompt_factory_->WaitForPermissionBubble(); + EXPECT_FALSE(accuracy_tab_helper->is_dialog_running_); +} diff --git a/browser/ui/geolocation/geolocation_accuracy_tab_helper.cc b/browser/ui/geolocation/geolocation_accuracy_tab_helper.cc new file mode 100644 index 000000000000..9f6900d31af4 --- /dev/null +++ b/browser/ui/geolocation/geolocation_accuracy_tab_helper.cc @@ -0,0 +1,81 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/ui/geolocation/geolocation_accuracy_tab_helper.h" + +#include "brave/browser/ui/browser_dialogs.h" +#include "brave/browser/ui/geolocation/pref_names.h" +#include "build/build_config.h" +#include "chrome/browser/ui/browser.h" +#include "components/prefs/pref_service.h" +#include "components/user_prefs/user_prefs.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" + +#if BUILDFLAG(IS_WIN) +#include "brave/browser/ui/geolocation/geolocation_accuracy_utils_win.h" +#endif + +// static +void GeolocationAccuracyTabHelper::MaybeCreateForWebContents( + content::WebContents* contents) { +#if BUILDFLAG(IS_WIN) + content::WebContentsUserData< + GeolocationAccuracyTabHelper>::CreateForWebContents(contents); +#endif +} + +GeolocationAccuracyTabHelper::GeolocationAccuracyTabHelper( + content::WebContents* contents) + : WebContentsUserData(*contents), WebContentsObserver(contents) {} + +GeolocationAccuracyTabHelper::~GeolocationAccuracyTabHelper() = default; + +void GeolocationAccuracyTabHelper::LaunchAccuracyHelperDialogIfNeeded() { + if (auto* prefs = + user_prefs::UserPrefs::Get(web_contents()->GetBrowserContext()); + !prefs->GetBoolean(kShowGeolocationAccuracyHelperDialog)) { + return; + } + + if (is_dialog_running_) { + return; + } + + if (dialog_asked_in_current_navigation_) { + return; + } + +#if BUILDFLAG(IS_WIN) + if (IsSystemLocationSettingEnabled()) { + DVLOG(2) << __func__ << " : system location service is enabled."; + return; + } +#endif + + dialog_asked_in_current_navigation_ = true; + is_dialog_running_ = true; + brave::ShowGeolocationAccuracyHelperDialog( + web_contents(), + base::BindOnce(&GeolocationAccuracyTabHelper::OnDialogClosed, + weak_ptr_factory_.GetWeakPtr())); +} + +void GeolocationAccuracyTabHelper::DidStartNavigation( + content::NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame() || + navigation_handle->IsSameDocument()) { + return; + } + + dialog_asked_in_current_navigation_ = false; +} + +void GeolocationAccuracyTabHelper::OnDialogClosed() { + is_dialog_running_ = false; +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(GeolocationAccuracyTabHelper); diff --git a/browser/ui/geolocation/geolocation_accuracy_tab_helper.h b/browser/ui/geolocation/geolocation_accuracy_tab_helper.h new file mode 100644 index 000000000000..47de3e50c5c0 --- /dev/null +++ b/browser/ui/geolocation/geolocation_accuracy_tab_helper.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_UI_GEOLOCATION_GEOLOCATION_ACCURACY_TAB_HELPER_H_ +#define BRAVE_BROWSER_UI_GEOLOCATION_GEOLOCATION_ACCURACY_TAB_HELPER_H_ + +#include "base/memory/weak_ptr.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +class GeolocationAccuracyTabHelper + : public content::WebContentsUserData, + public content::WebContentsObserver { + public: + static void MaybeCreateForWebContents(content::WebContents* contents); + + ~GeolocationAccuracyTabHelper() override; + + // Launches per-tab dialog. + void LaunchAccuracyHelperDialogIfNeeded(); + + // content::WebContentsObserver overrides: + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override; + + private: + friend WebContentsUserData; + FRIEND_TEST_ALL_PREFIXES(GeolocationAccuracyBrowserTest, DialogLaunchTest); + FRIEND_TEST_ALL_PREFIXES(GeolocationAccuracyBrowserTest, + DialogLaunchDisabledTest); + + explicit GeolocationAccuracyTabHelper(content::WebContents* contents); + + void OnDialogClosed(); + + bool dialog_asked_in_current_navigation_ = false; + bool is_dialog_running_ = false; + + base::WeakPtrFactory weak_ptr_factory_{this}; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +#endif // BRAVE_BROWSER_UI_GEOLOCATION_GEOLOCATION_ACCURACY_TAB_HELPER_H_ diff --git a/browser/ui/geolocation/geolocation_accuracy_utils_win.cc b/browser/ui/geolocation/geolocation_accuracy_utils_win.cc new file mode 100644 index 000000000000..7e22185678ec --- /dev/null +++ b/browser/ui/geolocation/geolocation_accuracy_utils_win.cc @@ -0,0 +1,68 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/ui/geolocation/geolocation_accuracy_utils_win.h" + +#include + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/string_piece.h" +#include "base/threading/scoped_blocking_call.h" +#include "base/threading/scoped_thread_priority.h" +#include "base/win/core_winrt_util.h" + +using ABI::Windows::Devices::Enumeration::DeviceAccessStatus; +using ABI::Windows::Devices::Enumeration::DeviceClass; +using ABI::Windows::Devices::Enumeration::IDeviceAccessInformation; +using ABI::Windows::Devices::Enumeration::IDeviceAccessInformationStatics; +using Microsoft::WRL::ComPtr; + +// Copied from services/device/geolocation/win/location_provider_winrt.cc +bool IsSystemLocationSettingEnabled() { + ComPtr dev_access_info_statics; + HRESULT hr = base::win::GetActivationFactory< + IDeviceAccessInformationStatics, + RuntimeClass_Windows_Devices_Enumeration_DeviceAccessInformation>( + &dev_access_info_statics); + if (FAILED(hr)) { + VLOG(1) << "IDeviceAccessInformationStatics failed: " << hr; + return true; + } + + ComPtr dev_access_info; + hr = dev_access_info_statics->CreateFromDeviceClass( + DeviceClass::DeviceClass_Location, &dev_access_info); + if (FAILED(hr)) { + VLOG(1) << "IDeviceAccessInformation failed: " << hr; + return true; + } + + auto status = DeviceAccessStatus::DeviceAccessStatus_Unspecified; + dev_access_info->get_CurrentStatus(&status); + + return !(status == DeviceAccessStatus::DeviceAccessStatus_DeniedBySystem || + status == DeviceAccessStatus::DeviceAccessStatus_DeniedByUser); +} + +void LaunchLocationServiceSettings() { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::WILL_BLOCK); + + SHELLEXECUTEINFO sei = {sizeof(sei)}; + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = L"open"; + sei.lpFile = L"ms-settings:privacy-location"; + + // Mitigate the issues caused by loading DLLs on a background thread + // (http://crbug/973868). + SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY(); + + ::ShellExecuteExW(&sei); +} diff --git a/browser/ui/geolocation/geolocation_accuracy_utils_win.h b/browser/ui/geolocation/geolocation_accuracy_utils_win.h new file mode 100644 index 000000000000..07aa6e9ceac7 --- /dev/null +++ b/browser/ui/geolocation/geolocation_accuracy_utils_win.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_UI_GEOLOCATION_GEOLOCATION_ACCURACY_UTILS_WIN_H_ +#define BRAVE_BROWSER_UI_GEOLOCATION_GEOLOCATION_ACCURACY_UTILS_WIN_H_ + +// True when system location service is available to applications. +bool IsSystemLocationSettingEnabled(); +void LaunchLocationServiceSettings(); + +#endif // BRAVE_BROWSER_UI_GEOLOCATION_GEOLOCATION_ACCURACY_UTILS_WIN_H_ diff --git a/browser/ui/geolocation/pref_names.h b/browser/ui/geolocation/pref_names.h new file mode 100644 index 000000000000..a0efda167326 --- /dev/null +++ b/browser/ui/geolocation/pref_names.h @@ -0,0 +1,12 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_UI_GEOLOCATION_PREF_NAMES_H_ +#define BRAVE_BROWSER_UI_GEOLOCATION_PREF_NAMES_H_ + +constexpr char kShowGeolocationAccuracyHelperDialog[] = + "brave.show_geolocation_accuracy_helper_dialog"; + +#endif // BRAVE_BROWSER_UI_GEOLOCATION_PREF_NAMES_H_ diff --git a/browser/ui/views/geolocation_accuracy_helper_dialog_view.cc b/browser/ui/views/geolocation_accuracy_helper_dialog_view.cc new file mode 100644 index 000000000000..a8b2c4535705 --- /dev/null +++ b/browser/ui/views/geolocation_accuracy_helper_dialog_view.cc @@ -0,0 +1,223 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/ui/views/geolocation_accuracy_helper_dialog_view.h" + +#include +#include +#include + +#include "base/functional/bind.h" +#include "base/functional/callback.h" +#include "base/task/thread_pool.h" +#include "brave/browser/ui/color/leo/colors.h" +#include "brave/browser/ui/geolocation/pref_names.h" +#include "brave/browser/ui/views/infobars/custom_styled_label.h" +#include "brave/grit/brave_generated_resources.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/browser_window.h" +#include "components/constrained_window/constrained_window_views.h" +#include "components/prefs/pref_service.h" +#include "components/user_prefs/user_prefs.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/gfx/font_list.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/label.h" +#include "ui/views/controls/link.h" +#include "ui/views/controls/styled_label.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/view_class_properties.h" + +#if BUILDFLAG(IS_WIN) +#include "brave/browser/ui/geolocation/geolocation_accuracy_utils_win.h" +#endif + +namespace brave { + +void ShowGeolocationAccuracyHelperDialog(content::WebContents* web_contents, + base::OnceClosure closing_callback) { + constrained_window::ShowWebModalDialogViews( + new GeolocationAccuracyHelperDialogView( + user_prefs::UserPrefs::Get(web_contents->GetBrowserContext()), + std::move(closing_callback)), + web_contents); +} + +} // namespace brave + +namespace { + +constexpr char kLearnMoreURL[] = + "https://support.microsoft.com/en-us/windows/" + "windows-location-service-and-privacy-3a8eee0a-5b0b-dc07-eede-2a5ca1c49088"; + +// Subclass for custom font. +class DontShowAgainCheckbox : public views::Checkbox { + public: + METADATA_HEADER(DontShowAgainCheckbox); + + using views::Checkbox::Checkbox; + ~DontShowAgainCheckbox() override = default; + DontShowAgainCheckbox(const DontShowAgainCheckbox&) = delete; + DontShowAgainCheckbox& operator=(const DontShowAgainCheckbox&) = delete; + + void SetFontList(const gfx::FontList& font_list) { + label()->SetFontList(font_list); + } +}; + +BEGIN_METADATA(DontShowAgainCheckbox, views::Checkbox) +END_METADATA + +} // namespace + +GeolocationAccuracyHelperDialogView::GeolocationAccuracyHelperDialogView( + PrefService* prefs, + base::OnceClosure closing_callback) + : prefs_(*prefs) { + RegisterWindowClosingCallback(std::move(closing_callback)); + set_should_ignore_snapping(true); + SetModalType(ui::MODAL_TYPE_CHILD); + SetShowCloseButton(false); + SetButtons(ui::DIALOG_BUTTON_OK); + + // Safe to use Unretained() here because this callback is only called before + // closing this widget. + SetAcceptCallback(base::BindOnce( + &GeolocationAccuracyHelperDialogView::OnAccept, base::Unretained(this))); + + constexpr int kPadding = 24; + SetLayoutManager(std::make_unique( + views::BoxLayout::Orientation::kVertical, + gfx::Insets(kPadding), kPadding)) + ->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kStart); +} + +GeolocationAccuracyHelperDialogView::~GeolocationAccuracyHelperDialogView() = + default; + +void GeolocationAccuracyHelperDialogView::AddedToWidget() { + SetupChildViews(); +} + +void GeolocationAccuracyHelperDialogView::OnWidgetInitialized() { + // dialog button should be accessed after widget initialized. + // See the comment of DialogDelegate::GetOkButton(). + GetOkButton()->SetKind(views::MdTextButton::kPrimary); + GetOkButton()->SetProminent(false); +} + +void GeolocationAccuracyHelperDialogView::SetupChildViews() { + auto* header_label = + AddChildView(std::make_unique(l10n_util::GetStringUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_HEADER_LABEL))); + header_label->SetFontList(gfx::FontList("SF Pro Text, Semi-Bold 16px")); + header_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + + std::vector offsets; + const std::u16string contents_text_part_one = l10n_util::GetStringUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_CONTENTS_PART_ONE_LABEL); + const std::u16string contents_text_part_two = l10n_util::GetStringUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_CONTENTS_PART_TWO_LABEL); + const std::u16string contents_text = l10n_util::GetStringFUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_CONTENTS_LABEL, + contents_text_part_one, contents_text_part_two, &offsets); + + auto* contents_label = AddChildView(std::make_unique()); + contents_label->SetText(contents_text); + views::StyledLabel::RangeStyleInfo part_style; + part_style.custom_font = gfx::FontList("SF Pro Text, Semi-Bold 14px"); + contents_label->AddStyleRange( + gfx::Range(offsets[0], offsets[0] + contents_text_part_one.length()), + part_style); + contents_label->AddStyleRange( + gfx::Range(offsets[1], offsets[1] + contents_text_part_two.length()), + part_style); + contents_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + constexpr int kMaxWidth = 445; + contents_label->SizeToFit(kMaxWidth); + + views::StyledLabel::RangeStyleInfo default_style; + default_style.custom_font = gfx::FontList("SF Pro Text, Normal 14px"); + contents_label->AddStyleRange(gfx::Range(0, offsets[0]), default_style); + contents_label->AddStyleRange( + gfx::Range(offsets[0] + contents_text_part_one.length(), offsets[1]), + default_style); + contents_label->AddStyleRange( + gfx::Range(offsets[1] + contents_text_part_two.length(), + contents_text.length()), + default_style); + + size_t offset; + const std::u16string learn_more_part_text = l10n_util::GetStringUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_LEARN_MORE_LABEL_PART); + const std::u16string contents_second_text = l10n_util::GetStringFUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_CONTENTS_SECOND_LABEL, + learn_more_part_text, &offset); + auto* contents_second_label = + AddChildView(std::make_unique()); + contents_second_label->SetText(contents_second_text); + contents_second_label->AddStyleRange(gfx::Range(0, offset), default_style); + + // Safe to use Unretained() here because this link is owned by this class. + views::StyledLabel::RangeStyleInfo learn_more_style = + views::StyledLabel::RangeStyleInfo::CreateForLink(base::BindRepeating( + &GeolocationAccuracyHelperDialogView::OnLearnMoreClicked, + base::Unretained(this))); + learn_more_style.custom_font = gfx::FontList("SF Pro Text, Normal 14px"); + learn_more_style.override_color = leo::GetColor( + leo::Color::kColorTextInteractive, GetNativeTheme()->ShouldUseDarkColors() + ? leo::Theme::kDark + : leo::Theme::kLight); + + contents_second_label->AddStyleRange( + gfx::Range(offset, offset + learn_more_part_text.length()), + learn_more_style); + contents_second_label->SizeToFit(kMaxWidth); + + // Using Unretained() is safe as |checkbox| is owned by this class. + auto* checkbox = AddChildView(std::make_unique( + l10n_util::GetStringUTF16( + IDS_GEOLOCATION_ACCURACY_HELPER_DLG_DONT_SHOW_AGAIN_LABEL), + base::BindRepeating( + &GeolocationAccuracyHelperDialogView::OnCheckboxUpdated, + base::Unretained(this)))); + checkbox->SetFontList(gfx::FontList("SF Pro Text, Normal 14px")); + dont_show_again_checkbox_ = checkbox; +} + +void GeolocationAccuracyHelperDialogView::OnCheckboxUpdated() { + prefs_->SetBoolean(kShowGeolocationAccuracyHelperDialog, + !dont_show_again_checkbox_->GetChecked()); +} + +void GeolocationAccuracyHelperDialogView::OnAccept() { +#if BUILDFLAG(IS_WIN) + base::ThreadPool::PostTask( + FROM_HERE, + {base::MayBlock(), base::TaskPriority::USER_BLOCKING, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + base::BindOnce(&LaunchLocationServiceSettings)); +#endif +} + +void GeolocationAccuracyHelperDialogView::OnLearnMoreClicked() { + // Using active window is fine here as this dialog is tied with active tab. + if (auto* browser = chrome::FindBrowserWithActiveWindow()) { + chrome::AddSelectedTabWithURL(browser, GURL(kLearnMoreURL), + ui::PAGE_TRANSITION_AUTO_TOPLEVEL); + } +} + +BEGIN_METADATA(GeolocationAccuracyHelperDialogView, views::DialogDelegateView) +END_METADATA diff --git a/browser/ui/views/geolocation_accuracy_helper_dialog_view.h b/browser/ui/views/geolocation_accuracy_helper_dialog_view.h new file mode 100644 index 000000000000..9d6aa3cbe92a --- /dev/null +++ b/browser/ui/views/geolocation_accuracy_helper_dialog_view.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_UI_VIEWS_GEOLOCATION_ACCURACY_HELPER_DIALOG_VIEW_H_ +#define BRAVE_BROWSER_UI_VIEWS_GEOLOCATION_ACCURACY_HELPER_DIALOG_VIEW_H_ + +#include "base/functional/callback_forward.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/raw_ref.h" +#include "ui/base/metadata/metadata_header_macros.h" +#include "ui/views/window/dialog_delegate.h" + +class PrefService; + +namespace views { +class Checkbox; +} // namespace views + +class GeolocationAccuracyHelperDialogView : public views::DialogDelegateView { + public: + METADATA_HEADER(GeolocationAccuracyHelperDialogView); + + GeolocationAccuracyHelperDialogView(PrefService* prefs, + base::OnceClosure closing_callback); + GeolocationAccuracyHelperDialogView( + const GeolocationAccuracyHelperDialogView&) = delete; + GeolocationAccuracyHelperDialogView& operator=( + const GeolocationAccuracyHelperDialogView&) = delete; + ~GeolocationAccuracyHelperDialogView() override; + + private: + // views::DialogDelegateView overrides: + void AddedToWidget() override; + void OnWidgetInitialized() override; + + void SetupChildViews(); + void OnCheckboxUpdated(); + void OnAccept(); + void OnLearnMoreClicked(); + + const raw_ref prefs_; + raw_ptr dont_show_again_checkbox_ = nullptr; +}; + +#endif // BRAVE_BROWSER_UI_VIEWS_GEOLOCATION_ACCURACY_HELPER_DIALOG_VIEW_H_ diff --git a/test/BUILD.gn b/test/BUILD.gn index 20086d8b92c0..f17e594eccbe 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -1118,6 +1118,7 @@ test("brave_browser_tests") { "//brave/app/theme:brave_theme_resources_grit", "//brave/app/theme:brave_unscaled_resources_grit", "//brave/browser/sharing_hub:browser_tests", + "//brave/browser/ui/geolocation:browser_tests", "//brave/browser/ui/whats_new:browser_test", "//chrome/browser/apps/app_service:app_service", "//chrome/browser/apps/app_service:constants",