diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/chrome/browser/ui/webui/settings/chromeos | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) |
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/chrome/browser/ui/webui/settings/chromeos')
64 files changed, 7354 insertions, 890 deletions
diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/DEPS b/chromium/chrome/browser/ui/webui/settings/chromeos/DEPS new file mode 100644 index 00000000000..eb3c6b1fa0c --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+chrome/services/local_search_service", + "+device/udev_linux/fake_udev_loader.h", # For keyboard unit test. +] diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS b/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS index f42990c5d35..ff0d48d0403 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/OWNERS @@ -1,3 +1,5 @@ +file://chrome/browser/resources/settings/chromeos/OWNERS + per-file multidevice_handler*=file://chromeos/components/multidevice/OWNERS -# COMPONENT: UI>Settings +# COMPONENT: OS>Systems>Settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc index 34dbb5b6fac..e1d62743ff2 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.cc @@ -4,8 +4,10 @@ #include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h" +#include "ash/public/cpp/tablet_mode.h" #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/metrics/histogram_functions.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" @@ -18,11 +20,24 @@ namespace chromeos { namespace settings { -AccessibilityHandler::AccessibilityHandler(content::WebUI* webui) - : profile_(Profile::FromWebUI(webui)) { +namespace { + +void RecordShowShelfNavigationButtonsValueChange(bool enabled) { + base::UmaHistogramBoolean( + "Accessibility.CrosShelfNavigationButtonsInTabletModeChanged." + "OsSettings", + enabled); } -AccessibilityHandler::~AccessibilityHandler() {} +} // namespace + +AccessibilityHandler::AccessibilityHandler(Profile* profile) + : profile_(profile) {} + +AccessibilityHandler::~AccessibilityHandler() { + if (a11y_nav_buttons_toggle_metrics_reporter_timer_.IsRunning()) + a11y_nav_buttons_toggle_metrics_reporter_timer_.FireNow(); +} void AccessibilityHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( @@ -35,13 +50,21 @@ void AccessibilityHandler::RegisterMessages() { &AccessibilityHandler::HandleShowSelectToSpeakSettings, base::Unretained(this))); web_ui()->RegisterMessageCallback( - "getStartupSoundEnabled", - base::BindRepeating(&AccessibilityHandler::HandleGetStartupSoundEnabled, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( "setStartupSoundEnabled", base::BindRepeating(&AccessibilityHandler::HandleSetStartupSoundEnabled, base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "recordSelectedShowShelfNavigationButtonValue", + base::BindRepeating( + &AccessibilityHandler:: + HandleRecordSelectedShowShelfNavigationButtonsValue, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "manageA11yPageReady", + base::BindRepeating(&AccessibilityHandler::HandleManageA11yPageReady, + base::Unretained(this))); } void AccessibilityHandler::HandleShowChromeVoxSettings( @@ -54,20 +77,54 @@ void AccessibilityHandler::HandleShowSelectToSpeakSettings( OpenExtensionOptionsPage(extension_misc::kSelectToSpeakExtensionId); } -void AccessibilityHandler::HandleGetStartupSoundEnabled( +void AccessibilityHandler::HandleSetStartupSoundEnabled( const base::ListValue* args) { - AllowJavascript(); - FireWebUIListener( - "startup-sound-enabled-updated", - base::Value(AccessibilityManager::Get()->GetStartupSoundEnabled())); + DCHECK_EQ(1U, args->GetSize()); + bool enabled; + args->GetBoolean(0, &enabled); + AccessibilityManager::Get()->SetStartupSoundEnabled(enabled); } -void AccessibilityHandler::HandleSetStartupSoundEnabled( +void AccessibilityHandler::HandleRecordSelectedShowShelfNavigationButtonsValue( const base::ListValue* args) { DCHECK_EQ(1U, args->GetSize()); bool enabled; args->GetBoolean(0, &enabled); - AccessibilityManager::Get()->SetStartupSoundEnabled(enabled); + + a11y_nav_buttons_toggle_metrics_reporter_timer_.Start( + FROM_HERE, base::TimeDelta::FromSeconds(10), + base::BindOnce(&RecordShowShelfNavigationButtonsValueChange, enabled)); +} + +void AccessibilityHandler::HandleManageA11yPageReady( + const base::ListValue* args) { + AllowJavascript(); + + // When tablet mode is active we can return early since tablet mode + // is supported. + if (ash::TabletMode::Get()->InTabletMode()) { + FireWebUIListener( + "initial-data-ready", + base::Value(AccessibilityManager::Get()->GetStartupSoundEnabled()), + base::Value(true /* tablet_mode_supported */)); + return; + } + + PowerManagerClient::Get()->GetSwitchStates( + base::BindOnce(&AccessibilityHandler::OnReceivedSwitchStates, + weak_ptr_factory_.GetWeakPtr())); +} + +void AccessibilityHandler::OnReceivedSwitchStates( + base::Optional<PowerManagerClient::SwitchStates> switch_states) { + bool tablet_mode_supported = + switch_states.has_value() && + switch_states->tablet_mode != PowerManagerClient::TabletMode::UNSUPPORTED; + + FireWebUIListener( + "initial-data-ready", + base::Value(AccessibilityManager::Get()->GetStartupSoundEnabled()), + base::Value(tablet_mode_supported)); } void AccessibilityHandler::OpenExtensionOptionsPage(const char extension_id[]) { diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h index 80d735832cb..54290f81dd8 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h @@ -6,16 +6,14 @@ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ACCESSIBILITY_HANDLER_H_ #include "base/macros.h" +#include "base/timer/timer.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" +#include "chromeos/dbus/power/power_manager_client.h" namespace base { class ListValue; } -namespace content { -class WebUI; -} - class Profile; namespace chromeos { @@ -23,7 +21,7 @@ namespace settings { class AccessibilityHandler : public ::settings::SettingsPageUIHandler { public: - explicit AccessibilityHandler(content::WebUI* webui); + explicit AccessibilityHandler(Profile* profile); ~AccessibilityHandler() override; // SettingsPageUIHandler implementation. @@ -31,18 +29,36 @@ class AccessibilityHandler : public ::settings::SettingsPageUIHandler { void OnJavascriptAllowed() override {} void OnJavascriptDisallowed() override {} + // Callback which updates if startup sound is enabled and if tablet + // mode is supported. Visible for testing. + void HandleManageA11yPageReady(const base::ListValue* args); + private: // Callback for the messages to show settings for ChromeVox or // Select To Speak. void HandleShowChromeVoxSettings(const base::ListValue* args); void HandleShowSelectToSpeakSettings(const base::ListValue* args); - void HandleGetStartupSoundEnabled(const base::ListValue* args); void HandleSetStartupSoundEnabled(const base::ListValue* args); + void HandleRecordSelectedShowShelfNavigationButtonsValue( + const base::ListValue* args); + + // Callback which updates visibility for the shelf navigation buttons + // accessibility setting, depending on whether tablet mode is supported. + void OnReceivedSwitchStates( + base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states); void OpenExtensionOptionsPage(const char extension_id[]); Profile* profile_; // Weak pointer. + // Timer to record user changed value for the accessibility setting to turn + // shelf navigation buttons on in tablet mode. The metric is recorded with 10 + // second delay to avoid overreporting when the user keeps toggling the + // setting value in the screen UI. + base::OneShotTimer a11y_nav_buttons_toggle_metrics_reporter_timer_; + + base::WeakPtrFactory<AccessibilityHandler> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(AccessibilityHandler); }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_unittest.cc new file mode 100644 index 00000000000..8ee78caacd8 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_handler_unittest.cc @@ -0,0 +1,103 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/accessibility_handler.h" + +#include <memory> + +#include "ash/public/cpp/test/test_tablet_mode.h" +#include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chromeos/dbus/power/fake_power_manager_client.h" +#include "content/public/test/test_web_ui.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace settings { + +class TestingAccessibilityHandler : public AccessibilityHandler { + public: + explicit TestingAccessibilityHandler(content::WebUI* web_ui) + : AccessibilityHandler(/* profile */ nullptr) { + set_web_ui(web_ui); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestingAccessibilityHandler); +}; + +class AccessibilityHandlerTest : public ChromeRenderViewHostTestHarness { + public: + AccessibilityHandlerTest() = default; + AccessibilityHandlerTest(const AccessibilityHandlerTest&) = delete; + AccessibilityHandlerTest& operator=(const AccessibilityHandlerTest&) = delete; + + void SetUp() override { + ChromeRenderViewHostTestHarness::SetUp(); + PowerManagerClient::InitializeFake(); + test_tablet_mode_ = std::make_unique<ash::TestTabletMode>(); + handler_ = std::make_unique<TestingAccessibilityHandler>(&web_ui_); + } + + void TearDown() override { + handler_.reset(); + test_tablet_mode_.reset(); + PowerManagerClient::Shutdown(); + ChromeRenderViewHostTestHarness::TearDown(); + } + + content::TestWebUI web_ui_; + std::unique_ptr<ash::TestTabletMode> test_tablet_mode_; + std::unique_ptr<TestingAccessibilityHandler> handler_; +}; + +// Test that when tablet mode is supported, the correct data is returned by +// HandleManageA11yPageReady(). +TEST_F(AccessibilityHandlerTest, ManageA11yPageReadyTabletModeSupported) { + // Set tablet mode as supported. + chromeos::FakePowerManagerClient::Get()->SetTabletMode( + chromeos::PowerManagerClient::TabletMode::OFF, base::TimeTicks()); + + handler_->HandleManageA11yPageReady(/* args */ nullptr); + + // Wait for the AccessibilityHandler to receive data from PowerManagerClient. + base::RunLoop().RunUntilIdle(); + + const content::TestWebUI::CallData& call_data = *web_ui_.call_data().back(); + + // Ensure tablet mode is returned as supported. + EXPECT_TRUE(call_data.arg3()->GetBool()); +} + +// Test that when tablet mode is unsupported, the correct data is returned by +// HandleManageA11yPageReady(). +TEST_F(AccessibilityHandlerTest, ManageA11yPageReadyTabletModeUnsupported) { + // Set tablet mode as unsupported. + chromeos::FakePowerManagerClient::Get()->SetTabletMode( + chromeos::PowerManagerClient::TabletMode::UNSUPPORTED, base::TimeTicks()); + handler_->HandleManageA11yPageReady(/* args */ nullptr); + + // Wait for the AccessibilityHandler to receive data from PowerManagerClient. + base::RunLoop().RunUntilIdle(); + + const content::TestWebUI::CallData& call_data = *web_ui_.call_data().back(); + + // Ensure tablet mode is returned as unsupported. + EXPECT_FALSE(call_data.arg3()->GetBool()); +} + +// Test that when tablet mode is enabled, the correct data is returned by +// HandleManageA11yPageReady(). +TEST_F(AccessibilityHandlerTest, ManageA11yPageReadyTabletModeEnabled) { + test_tablet_mode_->SetEnabledForTest(true); + handler_->HandleManageA11yPageReady(/* args */ nullptr); + + const content::TestWebUI::CallData& call_data = *web_ui_.call_data().back(); + + // Ensure tablet mode is returned as supported. + EXPECT_TRUE(call_data.arg3()->GetBool()); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc index a8b296703d0..e6d80c5a757 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc @@ -23,6 +23,7 @@ #include "chrome/browser/ui/webui/signin/inline_login_handler_dialog_chromeos.h" #include "chrome/grit/generated_resources.h" #include "chromeos/components/account_manager/account_manager_factory.h" +#include "components/signin/public/identity_manager/consent_level.h" #include "components/user_manager/user.h" #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -273,12 +274,13 @@ void AccountManagerUIHandler::OnGetAccounts( GetEnterpriseDomainFromUsername(user->GetDisplayEmail())); } else if (profile_->GetProfilePolicyConnector()->IsManaged()) { device_account.SetOrganization(GetEnterpriseDomainFromUsername( - identity_manager_->GetPrimaryAccountInfo().email)); + identity_manager_ + ->GetPrimaryAccountInfo(signin::ConsentLevel::kNotRequired) + .email)); } // Device account must show up at the top. - accounts.GetList().insert(accounts.GetList().begin(), - device_account.Build()); + accounts.Insert(accounts.GetList().begin(), device_account.Build()); } ResolveJavascriptCallback(callback_id, accounts); @@ -418,17 +420,26 @@ void AccountManagerUIHandler::OnAccountRemoved( RefreshUI(); } -// |signin::IdentityManager::Observer| overrides. For newly added accounts, -// |signin::IdentityManager| may take some time to fetch user's full name and -// account image. Whenever that is completed, we may need to update the UI with -// this new set of information. Note that we may be listening to -// |signin::IdentityManager| but we still consider |AccountManager| to be the -// source of truth for account list. +// |signin::IdentityManager::Observer| overrides. +// +// For newly added accounts, |signin::IdentityManager| may take some time to +// fetch user's full name and account image. Whenever that is completed, we may +// need to update the UI with this new set of information. Note that we may be +// listening to |signin::IdentityManager| but we still consider |AccountManager| +// to be the source of truth for account list. void AccountManagerUIHandler::OnExtendedAccountInfoUpdated( const AccountInfo& info) { RefreshUI(); } +void AccountManagerUIHandler::OnErrorStateOfRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info, + const GoogleServiceAuthError& error) { + if (error.state() != GoogleServiceAuthError::NONE) { + RefreshUI(); + } +} + void AccountManagerUIHandler::RefreshUI() { FireWebUIListener("accounts-changed"); } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h index 39f24fbbbec..21e8adf1bc8 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h @@ -44,6 +44,9 @@ class AccountManagerUIHandler : public ::settings::SettingsPageUIHandler, // |signin::IdentityManager::Observer| overrides. void OnExtendedAccountInfoUpdated(const AccountInfo& info) override; + void OnErrorStateOfRefreshTokenUpdatedForAccount( + const CoreAccountInfo& account_info, + const GoogleServiceAuthError& error) override; private: friend class AccountManagerUIHandlerTest; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc new file mode 100644 index 00000000000..e3c7e6d7645 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc @@ -0,0 +1,32 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/ambient_mode_handler.h" + +#include "base/bind.h" +#include "base/values.h" + +namespace chromeos { +namespace settings { + +AmbientModeHandler::AmbientModeHandler() = default; + +AmbientModeHandler::~AmbientModeHandler() = default; + +void AmbientModeHandler::RegisterMessages() { + web_ui()->RegisterMessageCallback( + "onAmbientModePageReady", + base::BindRepeating(&AmbientModeHandler::HandleInitialized, + base::Unretained(this))); +} + +void AmbientModeHandler::HandleInitialized(const base::ListValue* args) { + CHECK(args); + CHECK(args->empty()); + + AllowJavascript(); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h new file mode 100644 index 00000000000..a52b109160a --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h @@ -0,0 +1,39 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_AMBIENT_MODE_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_AMBIENT_MODE_HANDLER_H_ + +#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" + +namespace base { +class ListValue; +} // namespace base + +namespace chromeos { +namespace settings { + +// Chrome OS ambient mode settings page UI handler, to allow users to customize +// photo frame and other related functionalities. +class AmbientModeHandler : public ::settings::SettingsPageUIHandler { + public: + AmbientModeHandler(); + AmbientModeHandler(const AmbientModeHandler&) = delete; + AmbientModeHandler& operator=(const AmbientModeHandler&) = delete; + ~AmbientModeHandler() override; + + // settings::SettingsPageUIHandler: + void RegisterMessages() override; + void OnJavascriptAllowed() override {} + void OnJavascriptDisallowed() override {} + + private: + // WebUI call to signal js side is ready. + void HandleInitialized(const base::ListValue* args); +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_AMBIENT_MODE_HANDLER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc new file mode 100644 index 00000000000..c9cfb838ce3 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc @@ -0,0 +1,393 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/calculator/size_calculator.h" + +#include <numeric> + +#include "base/system/sys_info.h" +#include "base/task/post_task.h" +#include "base/task/thread_pool.h" +#include "base/values.h" +#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h" +#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h" +#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h" +#include "chrome/browser/browsing_data/browsing_data_database_helper.h" +#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h" +#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h" +#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h" +#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h" +#include "chrome/browser/chromeos/crostini/crostini_features.h" +#include "chrome/browser/chromeos/file_manager/path_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chromeos/cryptohome/cryptohome_util.h" +#include "chromeos/dbus/cryptohome/cryptohome_client.h" +#include "components/arc/arc_service_manager.h" +#include "components/arc/session/arc_bridge_service.h" +#include "components/arc/storage_manager/arc_storage_manager.h" +#include "components/browsing_data/content/conditional_cache_counting_helper.h" +#include "components/browsing_data/content/local_storage_helper.h" +#include "components/user_manager/user_manager.h" +#include "content/public/browser/storage_partition.h" + +namespace chromeos { +namespace settings { +namespace calculator { + +namespace { + +void GetSizeStatBlocking(const base::FilePath& mount_path, + int64_t* total_size, + int64_t* available_size) { + int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path); + if (size >= 0) + *total_size = size; + size = base::SysInfo::AmountOfFreeDiskSpace(mount_path); + if (size >= 0) + *available_size = size; +} + +} // namespace + +SizeCalculator::SizeCalculator(const CalculationType& calculation_type) + : calculation_type_(calculation_type) {} + +SizeCalculator::~SizeCalculator() {} + +void SizeCalculator::StartCalculation() { + if (calculating_) + return; + calculating_ = true; + PerformCalculation(); +} + +void SizeCalculator::AddObserver(SizeCalculator::Observer* observer) { + observers_.AddObserver(observer); +} + +void SizeCalculator::RemoveObserver(SizeCalculator::Observer* observer) { + observers_.RemoveObserver(observer); +} + +void SizeCalculator::NotifySizeCalculated( + int64_t total_bytes, + const base::Optional<int64_t>& available_bytes) { + calculating_ = false; + for (SizeCalculator::Observer& observer : observers_) { + observer.OnSizeCalculated(calculation_type_, total_bytes, available_bytes); + } +} + +SizeStatCalculator::SizeStatCalculator(Profile* profile) + : SizeCalculator(CalculationType::kInUse), profile_(profile) {} + +SizeStatCalculator::~SizeStatCalculator() = default; +void SizeStatCalculator::PerformCalculation() { + const base::FilePath my_files_path = + file_manager::util::GetMyFilesFolderForProfile(profile_); + + int64_t* total_size = new int64_t(0); + int64_t* available_size = new int64_t(0); + base::ThreadPool::PostTaskAndReply( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, + base::Bind(&GetSizeStatBlocking, my_files_path, total_size, + available_size), + base::Bind(&SizeStatCalculator::OnGetSizeStat, + weak_ptr_factory_.GetWeakPtr(), base::Owned(total_size), + base::Owned(available_size))); +} + +void SizeStatCalculator::OnGetSizeStat(int64_t* total_bytes, + int64_t* available_bytes) { + NotifySizeCalculated(*total_bytes, *available_bytes); +} + +MyFilesSizeCalculator::MyFilesSizeCalculator(Profile* profile) + : SizeCalculator(CalculationType::kMyFiles), profile_(profile) {} + +MyFilesSizeCalculator::~MyFilesSizeCalculator() = default; + +void MyFilesSizeCalculator::PerformCalculation() { + const base::FilePath my_files_path = + file_manager::util::GetMyFilesFolderForProfile(profile_); + + const base::FilePath android_files_path = + base::FilePath(file_manager::util::GetAndroidFilesPath()); + + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, + base::BindOnce(&MyFilesSizeCalculator::ComputeLocalFilesSize, + base::Unretained(this), my_files_path, android_files_path), + base::BindOnce(&MyFilesSizeCalculator::OnGetMyFilesSize, + weak_ptr_factory_.GetWeakPtr())); +} + +int64_t MyFilesSizeCalculator::ComputeLocalFilesSize( + const base::FilePath& my_files_path, + const base::FilePath& android_files_path) { + int64_t size = 0; + + // Compute directory size of My Files. + size += base::ComputeDirectorySize(my_files_path); + + // Compute directory size of Play Files. + size += base::ComputeDirectorySize(android_files_path); + + // Remove size of Download. If Android is enabled, the size of the Download + // folder is counted in both My Files and Play files. If Android is disabled, + // the Download folder doesn't exist and the returned size is 0. + const base::FilePath download_files_path = + android_files_path.AppendASCII("Download"); + size -= base::ComputeDirectorySize(download_files_path); + + return size; +} + +void MyFilesSizeCalculator::OnGetMyFilesSize(int64_t total_bytes) { + NotifySizeCalculated(total_bytes); +} + +BrowsingDataSizeCalculator::BrowsingDataSizeCalculator(Profile* profile) + : SizeCalculator(CalculationType::kBrowsingData), profile_(profile) {} + +BrowsingDataSizeCalculator::~BrowsingDataSizeCalculator() = default; + +void BrowsingDataSizeCalculator::PerformCalculation() { + has_browser_cache_size_ = false; + has_browser_site_data_size_ = false; + + // Fetch the size of http cache in browsing data. + browsing_data::ConditionalCacheCountingHelper::Count( + content::BrowserContext::GetDefaultStoragePartition(profile_), + base::Time(), base::Time::Max(), + base::BindOnce(&BrowsingDataSizeCalculator::OnGetCacheSize, + weak_ptr_factory_.GetWeakPtr())); + + // Fetch the size of site data in browsing data. + if (!site_data_size_collector_.get()) { + content::StoragePartition* storage_partition = + content::BrowserContext::GetDefaultStoragePartition(profile_); + site_data_size_collector_ = std::make_unique<SiteDataSizeCollector>( + storage_partition->GetPath(), + new BrowsingDataCookieHelper(storage_partition), + new BrowsingDataDatabaseHelper(profile_), + new browsing_data::LocalStorageHelper(profile_), + new BrowsingDataAppCacheHelper(storage_partition->GetAppCacheService()), + new BrowsingDataIndexedDBHelper(storage_partition), + BrowsingDataFileSystemHelper::Create( + storage_partition->GetFileSystemContext()), + new BrowsingDataServiceWorkerHelper( + storage_partition->GetServiceWorkerContext()), + new BrowsingDataCacheStorageHelper( + storage_partition->GetCacheStorageContext()), + BrowsingDataFlashLSOHelper::Create(profile_)); + } + site_data_size_collector_->Fetch( + base::Bind(&BrowsingDataSizeCalculator::OnGetBrowsingDataSize, + weak_ptr_factory_.GetWeakPtr(), /*is_site_data=*/true)); +} + +void BrowsingDataSizeCalculator::OnGetCacheSize(bool is_upper_limit, + int64_t size) { + DCHECK(!is_upper_limit); + OnGetBrowsingDataSize(/*is_site_data=*/false, size); +} + +void BrowsingDataSizeCalculator::OnGetBrowsingDataSize(bool is_site_data, + int64_t size) { + if (is_site_data) { + has_browser_site_data_size_ = true; + browser_site_data_size_ = size; + } else { + has_browser_cache_size_ = true; + browser_cache_size_ = size; + } + if (has_browser_cache_size_ && has_browser_site_data_size_) { + int64_t browsing_data_size; + if (browser_cache_size_ >= 0 && browser_site_data_size_ >= 0) { + browsing_data_size = browser_site_data_size_ + browser_cache_size_; + } else { + browsing_data_size = -1; + } + calculating_ = false; + NotifySizeCalculated(browsing_data_size); + } +} + +AppsSizeCalculator::AppsSizeCalculator(Profile* profile) + : SizeCalculator(CalculationType::kAppsExtensions), profile_(profile) {} + +AppsSizeCalculator::~AppsSizeCalculator() { + arc::ArcServiceManager::Get() + ->arc_bridge_service() + ->storage_manager() + ->RemoveObserver(this); +} + +void AppsSizeCalculator::OnConnectionReady() { + is_android_running_ = true; + StartCalculation(); +} + +void AppsSizeCalculator::OnConnectionClosed() { + is_android_running_ = false; +} + +void AppsSizeCalculator::AddObserver(SizeCalculator::Observer* observer) { + // Start observing arc mojo connection when the first observer is added, to + // allow the calculation of android apps. + if (!observers_.might_have_observers()) { + arc::ArcServiceManager::Get() + ->arc_bridge_service() + ->storage_manager() + ->AddObserver(this); + } + observers_.AddObserver(observer); +} + +void AppsSizeCalculator::RemoveObserver(SizeCalculator::Observer* observer) { + observers_.RemoveObserver(observer); + // Stop observing arc connection if all observers have been removed. + if (!observers_.might_have_observers()) { + arc::ArcServiceManager::Get() + ->arc_bridge_service() + ->storage_manager() + ->RemoveObserver(this); + } +} + +void AppsSizeCalculator::PerformCalculation() { + apps_extensions_size_ = 0; + has_apps_extensions_size_ = false; + android_apps_size_ = 0; + has_android_apps_size_ = false; + + UpdateAppsSize(); + UpdateAndroidAppsSize(); +} + +void AppsSizeCalculator::UpdateAppsSize() { + // Apps and extensions installed from the web store located in + // [user-hash]/Extensions. + const base::FilePath extensions_path = + profile_->GetPath().AppendASCII("Extensions"); + + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, + base::BindOnce(&base::ComputeDirectorySize, extensions_path), + base::BindOnce(&AppsSizeCalculator::OnGetAppsSize, + weak_ptr_factory_.GetWeakPtr())); +} + +void AppsSizeCalculator::OnGetAppsSize(int64_t total_bytes) { + apps_extensions_size_ = total_bytes; + has_apps_extensions_size_ = true; + UpdateAppsAndExtensionsSize(); +} + +void AppsSizeCalculator::UpdateAndroidAppsSize() { + if (!is_android_running_) { + has_android_apps_size_ = true; + UpdateAppsAndExtensionsSize(); + return; + } + + bool success = false; + auto* arc_storage_manager = + arc::ArcStorageManager::GetForBrowserContext(profile_); + if (arc_storage_manager) { + success = arc_storage_manager->GetApplicationsSize( + base::BindOnce(&AppsSizeCalculator::OnGetAndroidAppsSize, + weak_ptr_factory_.GetWeakPtr())); + } + if (!success) { + has_android_apps_size_ = true; + UpdateAppsAndExtensionsSize(); + } +} + +void AppsSizeCalculator::OnGetAndroidAppsSize( + bool succeeded, + arc::mojom::ApplicationsSizePtr size) { + has_android_apps_size_ = true; + if (succeeded) { + android_apps_size_ = size->total_code_bytes + size->total_data_bytes + + size->total_cache_bytes; + } + UpdateAppsAndExtensionsSize(); +} + +void AppsSizeCalculator::UpdateAppsAndExtensionsSize() { + if (has_apps_extensions_size_ && has_android_apps_size_) { + calculating_ = false; + NotifySizeCalculated(apps_extensions_size_ + android_apps_size_); + } +} + +CrostiniSizeCalculator::CrostiniSizeCalculator(Profile* profile) + : SizeCalculator(CalculationType::kCrostini), profile_(profile) {} + +CrostiniSizeCalculator::~CrostiniSizeCalculator() = default; + +void CrostiniSizeCalculator::PerformCalculation() { + if (!crostini::CrostiniFeatures::Get()->IsEnabled(profile_)) { + NotifySizeCalculated(0); + return; + } + + crostini::CrostiniManager::GetForProfile(profile_)->ListVmDisks( + base::BindOnce(&CrostiniSizeCalculator::OnGetCrostiniSize, + weak_ptr_factory_.GetWeakPtr())); +} + +void CrostiniSizeCalculator::OnGetCrostiniSize(crostini::CrostiniResult result, + int64_t total_bytes) { + calculating_ = false; + NotifySizeCalculated(total_bytes); +} + +OtherUsersSizeCalculator::OtherUsersSizeCalculator() + : SizeCalculator(CalculationType::kOtherUsers) {} + +OtherUsersSizeCalculator::~OtherUsersSizeCalculator() = default; + +void OtherUsersSizeCalculator::PerformCalculation() { + other_users_.clear(); + user_sizes_.clear(); + const user_manager::UserList& users = + user_manager::UserManager::Get()->GetUsers(); + for (auto* user : users) { + if (user->is_active()) + continue; + other_users_.push_back(user); + CryptohomeClient::Get()->GetAccountDiskUsage( + cryptohome::CreateAccountIdentifierFromAccountId(user->GetAccountId()), + base::BindOnce(&OtherUsersSizeCalculator::OnGetOtherUserSize, + weak_ptr_factory_.GetWeakPtr())); + } + // We should show "0 B" if there is no other user. + if (other_users_.empty()) { + NotifySizeCalculated(0); + } +} + +void OtherUsersSizeCalculator::OnGetOtherUserSize( + base::Optional<cryptohome::BaseReply> reply) { + user_sizes_.push_back(cryptohome::AccountDiskUsageReplyToUsageSize(reply)); + if (user_sizes_.size() != other_users_.size()) + return; + int64_t other_users_total_bytes; + // If all the requests succeed, shows the total bytes in the UI. + if (std::count(user_sizes_.begin(), user_sizes_.end(), -1) == 0) { + other_users_total_bytes = + std::accumulate(user_sizes_.begin(), user_sizes_.end(), 0LL); + } else { + other_users_total_bytes = -1; + } + NotifySizeCalculated(other_users_total_bytes); +} + +} // namespace calculator +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h new file mode 100644 index 00000000000..8f483f50b95 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h @@ -0,0 +1,296 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CALCULATOR_SIZE_CALCULATOR_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CALCULATOR_SIZE_CALCULATOR_H_ + +#include <array> +#include <bitset> +#include <memory> +#include <string> +#include <vector> + +#include "base/files/file_util.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list_types.h" +#include "base/values.h" +#include "chrome/browser/browsing_data/site_data_size_collector.h" +#include "chrome/browser/chromeos/crostini/crostini_manager.h" +#include "chromeos/dbus/cryptohome/rpc.pb.h" +#include "components/arc/mojom/storage_manager.mojom.h" +#include "components/arc/session/connection_observer.h" +#include "components/arc/storage_manager/arc_storage_manager.h" +#include "components/user_manager/user.h" + +class Profile; + +namespace chromeos { +namespace settings { +namespace calculator { + +// Base class for the calculation of a specific storage item. Instances of this +// class rely on their observers calling StartCalculation, and are designed to +// notify observers about the calculated sizes. +class SizeCalculator { + public: + // Enumeration listing the items displayed on the storage page. + enum class CalculationType { + kInUse = 0, + kMyFiles, + kBrowsingData, + kAppsExtensions, + kCrostini, + kOtherUsers, + kLast = kOtherUsers, + kSystem, + }; + + // Implement this interface to be notified about item size callbacks. + class Observer : public base::CheckedObserver { + public: + virtual void OnSizeCalculated( + const CalculationType& item_id, + int64_t total_bytes, + const base::Optional<int64_t>& available_bytes) = 0; + }; + + // Total number of storage items. + static constexpr int kCalculationTypeCount = + static_cast<int>(CalculationType::kLast) + 1; + + explicit SizeCalculator(const CalculationType& calculation_type); + virtual ~SizeCalculator(); + + // Starts the size calculation of a given storage item. + void StartCalculation(); + + // Adds an observer. + virtual void AddObserver(Observer* observer); + + // Removes an observer. + virtual void RemoveObserver(Observer* observer); + + protected: + // Performs the size calculation. + virtual void PerformCalculation() = 0; + + // Notify the StorageHandler about the calculated storage item size + void NotifySizeCalculated( + int64_t total_bytes, + const base::Optional<int64_t>& available_bytes = base::nullopt); + + // Item id. + const CalculationType calculation_type_; + + // Flag indicating that fetch operations for storage size are ongoing. + bool calculating_ = false; + // Observers being notified about storage items size changes. + base::ObserverList<SizeCalculator::Observer> observers_; +}; + +// Class handling the interactions with the filesystem to get storage +// statistics, using OnSizeStatCalculated to notify observers. +class SizeStatCalculator : public SizeCalculator { + public: + explicit SizeStatCalculator(Profile* profile); + ~SizeStatCalculator() override; + + SizeStatCalculator(const SizeStatCalculator&) = delete; + SizeStatCalculator& operator=(const SizeStatCalculator&) = delete; + + private: + friend class SizeStatTestAPI; + + void PerformCalculation() override; + + // Updates disk space information. + void OnGetSizeStat(int64_t* total_bytes, int64_t* available_bytes); + + Profile* profile_; + base::WeakPtrFactory<SizeStatCalculator> weak_ptr_factory_{this}; +}; + +// Class handling the calculation of the size of the user's personal files: My +// files + Android Play files. +class MyFilesSizeCalculator : public SizeCalculator { + public: + explicit MyFilesSizeCalculator(Profile* profile); + ~MyFilesSizeCalculator() override; + + MyFilesSizeCalculator(const MyFilesSizeCalculator&) = delete; + MyFilesSizeCalculator& operator=(const MyFilesSizeCalculator&) = delete; + + private: + friend class MyFilesSizeTestAPI; + + void PerformCalculation() override; + + // Computes the size of My Files and Play files. + int64_t ComputeLocalFilesSize(const base::FilePath& my_files_path, + const base::FilePath& android_files_path); + + // Updates the size of My Files and Play files. + void OnGetMyFilesSize(int64_t total_bytes); + + Profile* profile_; + base::WeakPtrFactory<MyFilesSizeCalculator> weak_ptr_factory_{this}; +}; + +// Class handling the calculation of browsing data and cache. +class BrowsingDataSizeCalculator : public SizeCalculator { + public: + explicit BrowsingDataSizeCalculator(Profile* profile); + ~BrowsingDataSizeCalculator() override; + + BrowsingDataSizeCalculator(const BrowsingDataSizeCalculator&) = delete; + BrowsingDataSizeCalculator& operator=(const BrowsingDataSizeCalculator&) = + delete; + + private: + friend class BrowsingDataSizeTestAPI; + + void PerformCalculation() override; + + // Callback to receive the cache size. + void OnGetCacheSize(bool is_upper_limit, int64_t size); + + // Callback to update the size of browsing data. + void OnGetBrowsingDataSize(bool is_site_data, int64_t size); + + // Total size of cache data in browsing data. + int64_t browser_cache_size_ = -1; + + // True if we have already received the size of http cache. + bool has_browser_cache_size_ = false; + + // Total size of site data in browsing data. + int64_t browser_site_data_size_ = -1; + + // True if we have already received the size of http cache. + bool has_browser_site_data_size_ = false; + + // Helper to compute the total size of all types of site date. + std::unique_ptr<SiteDataSizeCollector> site_data_size_collector_; + + Profile* profile_; + base::WeakPtrFactory<BrowsingDataSizeCalculator> weak_ptr_factory_{this}; +}; + +// Class handling the calculation of the size of the user's apps and extensions. +class AppsSizeCalculator + : public SizeCalculator, + public arc::ConnectionObserver<arc::mojom::StorageManagerInstance> { + public: + explicit AppsSizeCalculator(Profile* profile); + ~AppsSizeCalculator() override; + + AppsSizeCalculator(const AppsSizeCalculator&) = delete; + AppsSizeCalculator& operator=(const AppsSizeCalculator&) = delete; + + // arc::ConnectionObserver<arc::mojom::StorageManagerInstance>: + void OnConnectionReady() override; + void OnConnectionClosed() override; + + // Adds an observer. When the first observer is added, start observing the arc + // mojo connection UpdateAndroidAppsSize relies on. + void AddObserver(Observer* observer) override; + + // Removes an observer. When the last observer is removed, stop observing the + // arc mojo connection. + void RemoveObserver(Observer* observer) override; + + private: + friend class AppsSizeTestAPI; + + void PerformCalculation() override; + + // Requests updating the size of web store apps and extensions. + void UpdateAppsSize(); + + // Callback to update web store apps and extensions size. + void OnGetAppsSize(int64_t total_bytes); + + // Requests updating the size of android apps. + void UpdateAndroidAppsSize(); + + // Callback to update Android apps and cache. + void OnGetAndroidAppsSize(bool succeeded, + arc::mojom::ApplicationsSizePtr size); + + // Updates apps and extensions size. + void UpdateAppsAndExtensionsSize(); + + // Total size of apps and extensions + int64_t apps_extensions_size_ = 0; + + // True if we have already received the size of apps and extensions. + bool has_apps_extensions_size_ = false; + + // Total size of android apps + int64_t android_apps_size_ = 0; + + // True if we have already received the size of Android apps. + bool has_android_apps_size_ = false; + + // A flag for keeping track of the mojo connection status to the ARC + // container. + bool is_android_running_ = false; + + Profile* profile_; + base::WeakPtrFactory<AppsSizeCalculator> weak_ptr_factory_{this}; +}; + +// Class handling the calculation of crostini VM size. +class CrostiniSizeCalculator : public SizeCalculator { + public: + explicit CrostiniSizeCalculator(Profile* profile); + ~CrostiniSizeCalculator() override; + + CrostiniSizeCalculator(const CrostiniSizeCalculator&) = delete; + CrostiniSizeCalculator& operator=(const CrostiniSizeCalculator&) = delete; + + private: + friend class CrostiniSizeTestAPI; + + void PerformCalculation() override; + + // Callback to update the size of Crostini VMs. + void OnGetCrostiniSize(crostini::CrostiniResult result, int64_t size); + + Profile* profile_; + base::WeakPtrFactory<CrostiniSizeCalculator> weak_ptr_factory_{this}; +}; + +// Class handling the calculation of other users' cryptohomes. +class OtherUsersSizeCalculator : public SizeCalculator { + public: + OtherUsersSizeCalculator(); + ~OtherUsersSizeCalculator() override; + + OtherUsersSizeCalculator(const OtherUsersSizeCalculator&) = delete; + OtherUsersSizeCalculator& operator=(const OtherUsersSizeCalculator&) = delete; + + private: + friend class OtherUsersSizeTestAPI; + + void PerformCalculation() override; + + // Callback to update the sizes of the other users. + void OnGetOtherUserSize(base::Optional<cryptohome::BaseReply> reply); + + // The list of other users whose directory sizes will be accumulated as the + // size of "Other users". + user_manager::UserList other_users_; + + // Fetched sizes of user directories. + std::vector<int64_t> user_sizes_; + + base::WeakPtrFactory<OtherUsersSizeCalculator> weak_ptr_factory_{this}; +}; + +} // namespace calculator +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CALCULATOR_SIZE_CALCULATOR_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h new file mode 100644 index 00000000000..2e0850df244 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h @@ -0,0 +1,156 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_ + +#include <utility> + +#include "chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h" + +namespace chromeos { +namespace settings { +namespace calculator { + +class SizeStatTestAPI { + public: + explicit SizeStatTestAPI(StorageHandler* handler, + SizeStatCalculator* size_stat_calculator) { + size_stat_calculator_ = size_stat_calculator; + size_stat_calculator_->AddObserver(handler); + } + + void StartCalculation() { size_stat_calculator_->StartCalculation(); } + + void SimulateOnGetSizeStat(int64_t* total_size, int64_t* available_size) { + size_stat_calculator_->OnGetSizeStat(total_size, available_size); + } + + private: + SizeStatCalculator* size_stat_calculator_; +}; + +class MyFilesSizeTestAPI { + public: + explicit MyFilesSizeTestAPI(StorageHandler* handler, + MyFilesSizeCalculator* my_files_size_calculator) { + my_files_size_calculator_ = my_files_size_calculator; + my_files_size_calculator_->AddObserver(handler); + } + + void StartCalculation() { my_files_size_calculator_->StartCalculation(); } + + void SimulateOnGetTotalBytes(int64_t total_bytes) { + my_files_size_calculator_->NotifySizeCalculated(total_bytes); + } + + private: + MyFilesSizeCalculator* my_files_size_calculator_; +}; + +class BrowsingDataSizeTestAPI { + public: + explicit BrowsingDataSizeTestAPI( + StorageHandler* handler, + BrowsingDataSizeCalculator* browsing_data_size_calculator) { + browsing_data_size_calculator_ = browsing_data_size_calculator; + browsing_data_size_calculator_->AddObserver(handler); + } + + void StartCalculation() { + browsing_data_size_calculator_->StartCalculation(); + } + + void SimulateOnGetBrowsingDataSize(bool is_site_data, int64_t size) { + browsing_data_size_calculator_->OnGetBrowsingDataSize(is_site_data, size); + } + + private: + BrowsingDataSizeCalculator* browsing_data_size_calculator_; +}; + +class AppsSizeTestAPI { + public: + explicit AppsSizeTestAPI(StorageHandler* handler, + AppsSizeCalculator* apps_size_calculator) { + apps_size_calculator_ = apps_size_calculator; + apps_size_calculator_->AddObserver(handler); + } + + void StartCalculation() { apps_size_calculator_->StartCalculation(); } + + void SimulateOnGetAppsSize(int64_t total_bytes) { + apps_size_calculator_->OnGetAppsSize(total_bytes); + } + + void SimulateOnGetAndroidAppsSize(bool succeeded, + uint64_t total_code_bytes, + uint64_t total_data_bytes, + uint64_t total_cache_bytes) { + arc::mojom::ApplicationsSizePtr result( + ::arc::mojom::ApplicationsSize::New()); + result->total_code_bytes = total_code_bytes; + result->total_data_bytes = total_data_bytes; + result->total_cache_bytes = total_cache_bytes; + apps_size_calculator_->OnGetAndroidAppsSize(succeeded, std::move(result)); + } + + private: + AppsSizeCalculator* apps_size_calculator_; +}; + +class CrostiniSizeTestAPI { + public: + explicit CrostiniSizeTestAPI( + StorageHandler* handler, + CrostiniSizeCalculator* crostini_size_calculator) { + crostini_size_calculator_ = crostini_size_calculator; + crostini_size_calculator_->AddObserver(handler); + } + + void StartCalculation() { crostini_size_calculator_->StartCalculation(); } + + void SimulateOnGetCrostiniSize(int64_t size) { + crostini_size_calculator_->OnGetCrostiniSize( + crostini::CrostiniResult::SUCCESS, size); + } + + private: + CrostiniSizeCalculator* crostini_size_calculator_; +}; + +class OtherUsersSizeTestAPI { + public: + explicit OtherUsersSizeTestAPI( + StorageHandler* handler, + OtherUsersSizeCalculator* other_users_size_calculator) { + other_users_size_calculator_ = other_users_size_calculator; + other_users_size_calculator_->AddObserver(handler); + } + + void StartCalculation() { other_users_size_calculator_->StartCalculation(); } + + void InitializeOtherUserSize(int user_count) { + // When calling OnGetOtherUserSize, a callback is fired when + // user_sizes_.size() == other_users_.size(). We want to control the size of + // |other_users_|, rather than its content. This function initializes + // other_users_ as a list of |user_count| nullptrs. + other_users_size_calculator_->other_users_ = + user_manager::UserList(user_count); + } + + void SimulateOnGetOtherUserSize(base::Optional<cryptohome::BaseReply> reply) { + other_users_size_calculator_->OnGetOtherUserSize(reply); + } + + private: + OtherUsersSizeCalculator* other_users_size_calculator_; +}; + +} // namespace calculator +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_CALCULATOR_SIZE_CALCULATOR_TEST_API_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc index ed3cdc8465d..4bd641f16e6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.cc @@ -18,6 +18,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" +#include "base/task/thread_pool.h" #include "base/values.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" #include "chrome/browser/chromeos/camera_presence_notifier.h" @@ -228,8 +229,8 @@ void ChangePictureHandler::SendSelectedImage() { DCHECK(previous_image_.IsThreadSafe()); // Post a task because GetBitmapDataUrl does PNG encoding, which is // slow for large images. - base::PostTaskAndReplyWithResult( - FROM_HERE, {base::ThreadPool(), base::TaskPriority::USER_BLOCKING}, + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::TaskPriority::USER_BLOCKING}, base::BindOnce(&webui::GetBitmapDataUrl, *previous_image_.bitmap()), base::BindOnce(&ChangePictureHandler::SendOldImage, weak_ptr_factory_.GetWeakPtr())); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h index a3bb95d292e..1be240f6115 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h @@ -9,7 +9,7 @@ #include "base/memory/weak_ptr.h" #include "base/scoped_observer.h" #include "chrome/browser/chromeos/camera_presence_notifier.h" -#include "chrome/browser/image_decoder.h" +#include "chrome/browser/image_decoder/image_decoder.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "components/user_manager/user_manager.h" #include "ui/gfx/image/image_skia.h" diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc index 1c966904f49..1351b63b654 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc @@ -9,21 +9,27 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/callback_forward.h" #include "base/metrics/histogram_functions.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/browser_process_platform_part.h" +#include "chrome/browser/chromeos/crostini/crostini_disk.h" +#include "chrome/browser/chromeos/crostini/crostini_features.h" +#include "chrome/browser/chromeos/crostini/crostini_installer.h" +#include "chrome/browser/chromeos/crostini/crostini_port_forwarder.h" +#include "chrome/browser/chromeos/crostini/crostini_pref_names.h" +#include "chrome/browser/chromeos/crostini/crostini_types.mojom.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/file_manager/path_util.h" #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h" -#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/lifetime/application_lifetime.h" -#include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/browser_thread.h" +#include "ui/display/screen.h" namespace chromeos { namespace settings { @@ -105,11 +111,42 @@ void CrostiniHandler::RegisterMessages() { "disableArcAdbSideload", base::BindRepeating(&CrostiniHandler::HandleDisableArcAdbRequest, weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "requestCrostiniContainerUpgradeView", + base::BindRepeating(&CrostiniHandler::HandleRequestContainerUpgradeView, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "requestCrostiniUpgraderDialogStatus", + base::BindRepeating( + &CrostiniHandler::HandleCrostiniUpgraderDialogStatusRequest, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "requestCrostiniContainerUpgradeAvailable", + base::BindRepeating( + &CrostiniHandler::HandleCrostiniContainerUpgradeAvailableRequest, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "addCrostiniPortForward", + base::BindRepeating(&CrostiniHandler::HandleAddCrostiniPortForward, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "getCrostiniDiskInfo", + base::BindRepeating(&CrostiniHandler::HandleGetCrostiniDiskInfo, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "resizeCrostiniDisk", + base::BindRepeating(&CrostiniHandler::HandleResizeCrostiniDisk, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "checkCrostiniMicSharingStatus", + base::BindRepeating(&CrostiniHandler::HandleCheckCrostiniMicSharingStatus, + weak_ptr_factory_.GetWeakPtr())); } void CrostiniHandler::OnJavascriptAllowed() { - crostini::CrostiniManager::GetForProfile(profile_) - ->AddInstallerViewStatusObserver(this); + auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile_); + crostini_manager->AddCrostiniDialogStatusObserver(this); + crostini_manager->AddCrostiniContainerPropertiesObserver(this); if (chromeos::CrosUsbDetector::Get()) { chromeos::CrosUsbDetector::Get()->AddUsbDeviceObserver(this); } @@ -117,11 +154,9 @@ void CrostiniHandler::OnJavascriptAllowed() { } void CrostiniHandler::OnJavascriptDisallowed() { - if (crostini::CrostiniManager::GetForProfile(profile_) - ->HasInstallerViewStatusObserver(this)) { - crostini::CrostiniManager::GetForProfile(profile_) - ->RemoveInstallerViewStatusObserver(this); - } + auto* crostini_manager = crostini::CrostiniManager::GetForProfile(profile_); + crostini_manager->RemoveCrostiniDialogStatusObserver(this); + crostini_manager->RemoveCrostiniContainerPropertiesObserver(this); if (chromeos::CrosUsbDetector::Get()) { chromeos::CrosUsbDetector::Get()->RemoveUsbDeviceObserver(this); } @@ -131,8 +166,8 @@ void CrostiniHandler::OnJavascriptDisallowed() { void CrostiniHandler::HandleRequestCrostiniInstallerView( const base::ListValue* args) { AllowJavascript(); - ShowCrostiniInstallerView(Profile::FromWebUI(web_ui()), - crostini::CrostiniUISurface::kSettings); + crostini::CrostiniInstaller::GetForProfile(Profile::FromWebUI(web_ui())) + ->ShowDialog(crostini::CrostiniUISurface::kSettings); } void CrostiniHandler::HandleRequestRemoveCrostini(const base::ListValue* args) { @@ -144,40 +179,42 @@ void CrostiniHandler::HandleRequestRemoveCrostini(const base::ListValue* args) { void CrostiniHandler::HandleGetCrostiniSharedPathsDisplayText( const base::ListValue* args) { AllowJavascript(); - CHECK_EQ(2U, args->GetSize()); - std::string callback_id; - const base::ListValue* paths; - CHECK(args->GetString(0, &callback_id)); - CHECK(args->GetList(1, &paths)); + CHECK_EQ(2U, args->GetList().size()); + std::string callback_id = args->GetList()[0].GetString(); + base::Value::ConstListView paths = args->GetList()[1].GetList(); base::ListValue texts; - for (auto it = paths->begin(); it != paths->end(); ++it) { + for (size_t i = 0; i < paths.size(); ++i) { texts.AppendString(file_manager::util::GetPathDisplayTextForSettings( - profile_, it->GetString())); + profile_, paths[i].GetString())); } ResolveJavascriptCallback(base::Value(callback_id), texts); } void CrostiniHandler::HandleRemoveCrostiniSharedPath( const base::ListValue* args) { - CHECK_EQ(2U, args->GetSize()); - std::string vm_name; - CHECK(args->GetString(0, &vm_name)); - std::string path; - CHECK(args->GetString(1, &path)); + AllowJavascript(); + CHECK_EQ(3U, args->GetList().size()); + std::string callback_id = args->GetList()[0].GetString(); + std::string vm_name = args->GetList()[1].GetString(); + std::string path = args->GetList()[2].GetString(); guest_os::GuestOsSharePath::GetForProfile(profile_)->UnsharePath( vm_name, base::FilePath(path), /*unpersist=*/true, - base::BindOnce( - [](const std::string& path, bool result, - const std::string& failure_reason) { - if (!result) { - LOG(ERROR) << "Error unsharing " << path << ": " - << failure_reason; - } - }, - path)); + base::BindOnce(&CrostiniHandler::OnCrostiniSharedPathRemoved, + weak_ptr_factory_.GetWeakPtr(), callback_id, path)); +} + +void CrostiniHandler::OnCrostiniSharedPathRemoved( + const std::string& callback_id, + const std::string& path, + bool result, + const std::string& failure_reason) { + if (!result) { + LOG(ERROR) << "Error unsharing " << path << ": " << failure_reason; + } + ResolveJavascriptCallback(base::Value(callback_id), base::Value(result)); } namespace { @@ -195,12 +232,35 @@ base::ListValue UsbDevicesToListValue( } return usb_devices_list; } + +base::Value CrostiniDiskInfoToValue( + std::unique_ptr<crostini::CrostiniDiskInfo> disk_info) { + base::Value disk_value(base::Value::Type::DICTIONARY); + if (!disk_info) { + disk_value.SetBoolKey("succeeded", false); + return disk_value; + } + disk_value.SetBoolKey("succeeded", true); + disk_value.SetBoolKey("canResize", disk_info->can_resize); + disk_value.SetBoolKey("isUserChosenSize", disk_info->is_user_chosen_size); + disk_value.SetIntKey("defaultIndex", disk_info->default_index); + base::Value ticks(base::Value::Type::LIST); + for (const auto& tick : disk_info->ticks) { + base::Value t(base::Value::Type::DICTIONARY); + t.SetDoubleKey("value", static_cast<double>(tick->value)); + t.SetStringKey("ariaValue", tick->aria_value); + t.SetStringKey("label", tick->label); + ticks.Append(std::move(t)); + } + disk_value.SetKey("ticks", std::move(ticks)); + return disk_value; +} } // namespace void CrostiniHandler::HandleGetCrostiniSharedUsbDevices( const base::ListValue* args) { AllowJavascript(); - CHECK_EQ(1U, args->GetSize()); + CHECK_EQ(1U, args->GetList().size()); std::string callback_id = args->GetList()[0].GetString(); @@ -217,7 +277,7 @@ void CrostiniHandler::HandleGetCrostiniSharedUsbDevices( void CrostiniHandler::HandleSetCrostiniUsbDeviceShared( const base::ListValue* args) { - CHECK_EQ(2U, args->GetSize()); + CHECK_EQ(2U, args->GetList().size()); const auto& args_list = args->GetList(); std::string guid = args_list[0].GetString(); bool shared = args_list[1].GetBool(); @@ -245,14 +305,14 @@ void CrostiniHandler::OnUsbDevicesChanged() { void CrostiniHandler::HandleExportCrostiniContainer( const base::ListValue* args) { - CHECK_EQ(0U, args->GetSize()); + CHECK_EQ(0U, args->GetList().size()); crostini::CrostiniExportImport::GetForProfile(profile_)->ExportContainer( web_ui()->GetWebContents()); } void CrostiniHandler::HandleImportCrostiniContainer( const base::ListValue* args) { - CHECK_EQ(0U, args->GetSize()); + CHECK_EQ(0U, args->GetList().size()); crostini::CrostiniExportImport::GetForProfile(profile_)->ImportContainer( web_ui()->GetWebContents()); } @@ -260,45 +320,57 @@ void CrostiniHandler::HandleImportCrostiniContainer( void CrostiniHandler::HandleCrostiniInstallerStatusRequest( const base::ListValue* args) { AllowJavascript(); - CHECK_EQ(0U, args->GetSize()); + CHECK_EQ(0U, args->GetList().size()); bool status = crostini::CrostiniManager::GetForProfile(profile_) - ->GetInstallerViewStatus(); - OnCrostiniInstallerViewStatusChanged(status); + ->GetCrostiniDialogStatus(crostini::DialogType::INSTALLER); + OnCrostiniDialogStatusChanged(crostini::DialogType::INSTALLER, status); } void CrostiniHandler::HandleCrostiniExportImportOperationStatusRequest( const base::ListValue* args) { AllowJavascript(); - CHECK_EQ(0U, args->GetSize()); + CHECK_EQ(0U, args->GetList().size()); bool in_progress = crostini::CrostiniExportImport::GetForProfile(profile_) ->GetExportImportOperationStatus(); OnCrostiniExportImportOperationStatusChanged(in_progress); } -void CrostiniHandler::OnCrostiniInstallerViewStatusChanged(bool status) { +void CrostiniHandler::OnCrostiniDialogStatusChanged( + crostini::DialogType dialog_type, + bool status) { // It's technically possible for this to be called before Javascript is // enabled, in which case we must not call FireWebUIListener if (IsJavascriptAllowed()) { // Other side listens with cr.addWebUIListener - FireWebUIListener("crostini-installer-status-changed", base::Value(status)); + switch (dialog_type) { + case crostini::DialogType::INSTALLER: + FireWebUIListener("crostini-installer-status-changed", + base::Value(status)); + break; + case crostini::DialogType::UPGRADER: + FireWebUIListener("crostini-upgrader-status-changed", + base::Value(status)); + break; + case crostini::DialogType::REMOVER: + FireWebUIListener("crostini-remover-status-changed", + base::Value(status)); + break; + default: + NOTREACHED(); + break; + } } } -void CrostiniHandler::OnCrostiniExportImportOperationStatusChanged( - bool in_progress) { - // Other side listens with cr.addWebUIListener - FireWebUIListener("crostini-export-import-operation-status-changed", - base::Value(in_progress)); -} - -void CrostiniHandler::HandleQueryArcAdbRequest(const base::ListValue* args) { - AllowJavascript(); - CHECK_EQ(0U, args->GetSize()); - - chromeos::SessionManagerClient* client = - chromeos::SessionManagerClient::Get(); - client->QueryAdbSideload(base::Bind(&CrostiniHandler::OnQueryAdbSideload, - weak_ptr_factory_.GetWeakPtr())); +void CrostiniHandler::OnContainerOsReleaseChanged( + const crostini::ContainerId& container_id, + bool can_upgrade) { + if (crostini::CrostiniFeatures::Get()->IsContainerUpgradeUIAllowed( + profile_) && + container_id == crostini::DefaultContainerId()) { + FireWebUIListener("crostini-container-upgrade-available-changed", + base::Value(can_upgrade)); + } } void CrostiniHandler::OnQueryAdbSideload( @@ -317,7 +389,7 @@ void CrostiniHandler::OnQueryAdbSideload( } void CrostiniHandler::HandleEnableArcAdbRequest(const base::ListValue* args) { - CHECK_EQ(0U, args->GetSize()); + CHECK_EQ(0U, args->GetList().size()); if (!CheckEligibilityToChangeArcAdbSideloading()) return; @@ -331,7 +403,7 @@ void CrostiniHandler::HandleEnableArcAdbRequest(const base::ListValue* args) { } void CrostiniHandler::HandleDisableArcAdbRequest(const base::ListValue* args) { - CHECK_EQ(0U, args->GetSize()); + CHECK_EQ(0U, args->GetList().size()); if (!CheckEligibilityToChangeArcAdbSideloading()) return; @@ -346,28 +418,133 @@ void CrostiniHandler::HandleDisableArcAdbRequest(const base::ListValue* args) { } bool CrostiniHandler::CheckEligibilityToChangeArcAdbSideloading() const { - if (!chromeos::ProfileHelper::IsOwnerProfile(profile_)) { - DVLOG(1) << "Only the owner can change adb sideloading status"; - return false; - } + return crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(profile_); +} - if (user_manager::UserManager::Get()->IsLoggedInAsChildUser()) { - DVLOG(1) << "adb sideloading is currently unsupported for child account"; - return false; - } +void CrostiniHandler::LaunchTerminal() { + crostini::LaunchCrostiniApp( + profile_, crostini::GetTerminalId(), + display::Screen::GetScreen()->GetPrimaryDisplay().id()); +} - if (profile_->GetProfilePolicyConnector()->IsManaged()) { - DVLOG(1) << "adb sideloading is currently unsupported for managed user"; - return false; - } +void CrostiniHandler::HandleRequestContainerUpgradeView( + const base::ListValue* args) { + CHECK_EQ(0U, args->GetList().size()); + chromeos::CrostiniUpgraderDialog::Show( + base::BindOnce(&CrostiniHandler::LaunchTerminal, + weak_ptr_factory_.GetWeakPtr()), + // If the user cancels the upgrade, we won't need to restart Crostini and + // we don't want to run the launch closure which would launch Terminal. + /*only_run_launch_closure_on_restart=*/true); +} - policy::BrowserPolicyConnectorChromeOS* connector = - g_browser_process->platform_part()->browser_policy_connector_chromeos(); - if (connector->IsEnterpriseManaged()) { - DVLOG(1) << "adb sideloading is currently unsupported on managed device"; - return false; - } - return true; +void CrostiniHandler::OnCrostiniExportImportOperationStatusChanged( + bool in_progress) { + // Other side listens with cr.addWebUIListener + FireWebUIListener("crostini-export-import-operation-status-changed", + base::Value(in_progress)); +} + +void CrostiniHandler::HandleQueryArcAdbRequest(const base::ListValue* args) { + AllowJavascript(); + CHECK_EQ(0U, args->GetList().size()); + + chromeos::SessionManagerClient* client = + chromeos::SessionManagerClient::Get(); + client->QueryAdbSideload(base::Bind(&CrostiniHandler::OnQueryAdbSideload, + weak_ptr_factory_.GetWeakPtr())); +} + +void CrostiniHandler::HandleCrostiniUpgraderDialogStatusRequest( + const base::ListValue* args) { + AllowJavascript(); + CHECK_EQ(0U, args->GetList().size()); + bool is_open = crostini::CrostiniManager::GetForProfile(profile_) + ->GetCrostiniDialogStatus(crostini::DialogType::UPGRADER); + OnCrostiniDialogStatusChanged(crostini::DialogType::UPGRADER, is_open); +} + +void CrostiniHandler::HandleCrostiniContainerUpgradeAvailableRequest( + const base::ListValue* args) { + AllowJavascript(); + + bool can_upgrade = crostini::ShouldAllowContainerUpgrade(profile_); + OnContainerOsReleaseChanged(crostini::DefaultContainerId(), can_upgrade); +} + +void CrostiniHandler::HandleAddCrostiniPortForward( + const base::ListValue* args) { + CHECK_EQ(6U, args->GetList().size()); + + std::string callback_id = args->GetList()[0].GetString(); + std::string vm_name = args->GetList()[1].GetString(); + std::string container_name = args->GetList()[2].GetString(); + int port_number = args->GetList()[3].GetInt(); + int protocol_type = args->GetList()[4].GetInt(); + std::string label = args->GetList()[5].GetString(); + + crostini::CrostiniPortForwarder::GetForProfile(profile_)->AddPort( + crostini::ContainerId(std::move(vm_name), std::move(container_name)), + port_number, + static_cast<crostini::CrostiniPortForwarder::Protocol>(protocol_type), + std::move(label), + base::Bind(&CrostiniHandler::OnPortForwardComplete, + weak_ptr_factory_.GetWeakPtr(), std::move(callback_id))); +} + +void CrostiniHandler::OnPortForwardComplete(std::string callback_id, + bool success) { + ResolveJavascriptCallback(base::Value(callback_id), base::Value(success)); +} + +void CrostiniHandler::ResolveGetCrostiniDiskInfoCallback( + const std::string& callback_id, + std::unique_ptr<crostini::CrostiniDiskInfo> disk_info) { + ResolveJavascriptCallback(base::Value(std::move(callback_id)), + CrostiniDiskInfoToValue(std::move(disk_info))); +} + +void CrostiniHandler::HandleGetCrostiniDiskInfo(const base::ListValue* args) { + AllowJavascript(); + CHECK_EQ(2U, args->GetList().size()); + std::string callback_id = args->GetList()[0].GetString(); + std::string vm_name = args->GetList()[1].GetString(); + crostini::disk::GetDiskInfo( + base::BindOnce(&CrostiniHandler::ResolveGetCrostiniDiskInfoCallback, + weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)), + profile_, std::move(vm_name)); +} + +void CrostiniHandler::HandleResizeCrostiniDisk(const base::ListValue* args) { + CHECK_EQ(3U, args->GetList().size()); + std::string callback_id = args->GetList()[0].GetString(); + std::string vm_name = args->GetList()[1].GetString(); + double bytes = args->GetList()[2].GetDouble(); + crostini::disk::ResizeCrostiniDisk( + profile_, std::move(vm_name), bytes, + base::BindOnce(&CrostiniHandler::ResolveResizeCrostiniDiskCallback, + weak_ptr_factory_.GetWeakPtr(), std::move(callback_id))); +} + +void CrostiniHandler::ResolveResizeCrostiniDiskCallback( + const std::string& callback_id, + bool succeeded) { + ResolveJavascriptCallback(base::Value(std::move(callback_id)), + base::Value(succeeded)); +} + +void CrostiniHandler::HandleCheckCrostiniMicSharingStatus( + const base::ListValue* args) { + CHECK_EQ(2U, args->GetList().size()); + std::string callback_id = args->GetList()[0].GetString(); + bool proposed_value = args->GetList()[1].GetBool(); + bool requiresRestart = + crostini::IsCrostiniRunning(profile_) && + profile_->GetPrefs()->GetBoolean( + crostini::prefs::kCrostiniMicSharingAtLastLaunch) != proposed_value; + + ResolveJavascriptCallback(base::Value(std::move(callback_id)), + base::Value(requiresRestart)); } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h index bab8509806a..f51fc69e1b7 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h @@ -18,14 +18,16 @@ class Profile; namespace crostini { enum class CrostiniResult; -} +struct CrostiniDiskInfo; +} // namespace crostini namespace chromeos { namespace settings { class CrostiniHandler : public ::settings::SettingsPageUIHandler, - public crostini::InstallerViewStatusObserver, + public crostini::CrostiniDialogStatusObserver, public crostini::CrostiniExportImport::Observer, + public crostini::CrostiniContainerPropertiesObserver, public chromeos::CrosUsbDeviceObserver { public: explicit CrostiniHandler(Profile* profile); @@ -45,6 +47,10 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, void HandleGetCrostiniSharedPathsDisplayText(const base::ListValue* args); // Remove a specified path from being shared. void HandleRemoveCrostiniSharedPath(const base::ListValue* args); + void OnCrostiniSharedPathRemoved(const std::string& callback_id, + const std::string& path, + bool result, + const std::string& failure_reason); // Returns a list of available USB devices. void HandleGetCrostiniSharedUsbDevices(const base::ListValue* args); // Set the share state of a USB device. @@ -57,8 +63,12 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, void HandleImportCrostiniContainer(const base::ListValue* args); // Handle a request for the CrostiniInstallerView status. void HandleCrostiniInstallerStatusRequest(const base::ListValue* args); - // Handle the CrostiniInstallerView opening/closing. - void OnCrostiniInstallerViewStatusChanged(bool open) override; + // crostini::CrostiniDialogStatusObserver + void OnCrostiniDialogStatusChanged(crostini::DialogType dialog_type, + bool open) override; + // crostini::CrostiniContainerPropertiesObserver + void OnContainerOsReleaseChanged(const crostini::ContainerId& container_id, + bool can_upgrade) override; // Handle a request for the CrostiniExportImport operation status. void HandleCrostiniExportImportOperationStatusRequest( const base::ListValue* args); @@ -70,6 +80,10 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, void HandleEnableArcAdbRequest(const base::ListValue* args); // Handle a request for disabling adb sideloading in ARC. void HandleDisableArcAdbRequest(const base::ListValue* args); + // Launch the Crostini terminal. + void LaunchTerminal(); + // Handle a request for showing the container upgrade view. + void HandleRequestContainerUpgradeView(const base::ListValue* args); // Callback of HandleQueryArcAdbRequest. void OnQueryAdbSideload( SessionManagerClient::AdbSideloadResponseCode response_code, @@ -77,6 +91,26 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, // Returns whether the current user can change adb sideloading configuration // on current device. bool CheckEligibilityToChangeArcAdbSideloading() const; + // Handle a request for the CrostiniUpgraderDialog status. + void HandleCrostiniUpgraderDialogStatusRequest(const base::ListValue* args); + // Handle a request for the availability of a container upgrade. + void HandleCrostiniContainerUpgradeAvailableRequest( + const base::ListValue* args); + // Handles a request for forwarding a new port. + void HandleAddCrostiniPortForward(const base::ListValue* args); + // Callback of port forwarding requests. + void OnPortForwardComplete(std::string callback_id, bool success); + // Fetches disk info for a VM, can be slow (seconds). + void HandleGetCrostiniDiskInfo(const base::ListValue* args); + void ResolveGetCrostiniDiskInfoCallback( + const std::string& callback_id, + std::unique_ptr<crostini::CrostiniDiskInfo> disk_info); + // Handles a request to resize a Crostini disk. + void HandleResizeCrostiniDisk(const base::ListValue* args); + void ResolveResizeCrostiniDiskCallback(const std::string& callback_id, + bool succeeded); + // Checks if a restart is required to update mic sharing settings. + void HandleCheckCrostiniMicSharingStatus(const base::ListValue* args); Profile* profile_; // weak_ptr_factory_ should always be last member. diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc index 74f1ab84039..fd6f8acdd11 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc @@ -14,12 +14,14 @@ #include "base/files/file_util.h" #include "base/json/json_string_value_serializer.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/optional.h" #include "base/path_service.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/task/post_task.h" +#include "base/task/thread_pool.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/values.h" #include "chrome/browser/browser_process.h" @@ -37,12 +39,11 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/chrome_select_file_policy.h" +#include "chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" -#include "chrome/common/webui_url_constants.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/debug_daemon/debug_daemon_client.h" -#include "chromeos/printing/ppd_cache.h" #include "chromeos/printing/ppd_line_reader.h" #include "chromeos/printing/printer_configuration.h" #include "chromeos/printing/printer_translator.h" @@ -58,6 +59,7 @@ #include "net/base/ip_endpoint.h" #include "net/url_request/url_request_context_getter.h" #include "printing/backend/print_backend.h" +#include "printing/printer_status.h" #include "url/gurl.h" namespace chromeos { @@ -113,8 +115,8 @@ void QueryAutoconf(const std::string& printer_uri, // Behavior for querying a non-IPP uri is undefined and disallowed. if (!IsIppUri(printer_uri) || !optional.has_value()) { PRINTER_LOG(ERROR) << "Printer uri is invalid: " << printer_uri; - std::move(callback).Run(PrinterQueryResult::UNKNOWN_FAILURE, "", "", "", {}, - false); + std::move(callback).Run(PrinterQueryResult::UNKNOWN_FAILURE, + printing::PrinterStatus(), "", "", "", {}, false); return; } @@ -168,6 +170,7 @@ std::unique_ptr<chromeos::Printer> DictToPrinter( std::string printer_make_and_model; std::string printer_address; std::string printer_protocol; + std::string print_server_uri; if (!printer_dict.GetString("printerId", &printer_id) || !printer_dict.GetString("printerName", &printer_name) || @@ -176,7 +179,8 @@ std::unique_ptr<chromeos::Printer> DictToPrinter( !printer_dict.GetString("printerModel", &printer_model) || !printer_dict.GetString("printerMakeAndModel", &printer_make_and_model) || !printer_dict.GetString("printerAddress", &printer_address) || - !printer_dict.GetString("printerProtocol", &printer_protocol)) { + !printer_dict.GetString("printerProtocol", &printer_protocol) || + !printer_dict.GetString("printServerUri", &print_server_uri)) { return nullptr; } @@ -195,6 +199,7 @@ std::unique_ptr<chromeos::Printer> DictToPrinter( printer->set_model(printer_model); printer->set_make_and_model(printer_make_and_model); printer->set_uri(printer_uri); + printer->set_print_server_uri(print_server_uri); return printer; } @@ -237,11 +242,6 @@ void SetPpdReference(const Printer::PpdReference& ppd_ref, base::Value* info) { } } -bool IsFilledPpdReference(const Printer::PpdReference& ppd_ref) { - return ppd_ref.autoconf || !ppd_ref.user_supplied_ppd_url.empty() || - !ppd_ref.effective_make_and_model.empty(); -} - Printer::PpdReference GetPpdReference(const base::Value* info) { const char ppd_ref_pathname[] = "printerPpdReference"; auto* user_supplied_ppd_url = @@ -267,40 +267,11 @@ Printer::PpdReference GetPpdReference(const base::Value* info) { return ret; } -bool ConvertToGURL(const std::string& url, GURL* gurl) { - *gurl = GURL(url); - if (!gurl->is_valid()) { - // URL is not valid. - return false; - } - if (!gurl->SchemeIsHTTPOrHTTPS() && !gurl->SchemeIs("ipp") && - !gurl->SchemeIs("ipps")) { - // URL has unsupported scheme; we support only: http, https, ipp, ipps. - return false; - } - // Replaces ipp/ipps by http/https. IPP standard describes protocol built - // on top of HTTP, so both types of addresses have the same meaning in the - // context of IPP interface. Moreover, the URL must have http/https scheme - // to pass IsStandard() test from GURL library (see "Validation of the URL - // address" below). - bool set_ipp_port = false; - if (gurl->SchemeIs("ipp")) { - set_ipp_port = (gurl->IntPort() == url::PORT_UNSPECIFIED); - *gurl = GURL("http" + url.substr(url.find_first_of(':'))); - } else if (gurl->SchemeIs("ipps")) { - *gurl = GURL("https" + url.substr(url.find_first_of(':'))); - } - // The default port for ipp is 631. If the schema ipp is replaced by http - // and the port is not explicitly defined in the url, we have to overwrite - // the default http port with the default ipp port. For ipps we do nothing - // because implementers use the same port for ipps and https. - if (set_ipp_port) { - GURL::Replacements replacement; - replacement.SetPortStr("631"); - *gurl = gurl->ReplaceComponents(replacement); - } - // Validation of the URL address. - return gurl->IsStandard(); +GURL GenerateHttpCupsServerUrl(const GURL& server_url) { + GURL::Replacements replacement; + replacement.SetSchemeStr("http"); + replacement.SetPortStr("631"); + return server_url.ReplaceComponents(replacement); } } // namespace @@ -405,6 +376,10 @@ void CupsPrintersHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( "getEulaUrl", base::BindRepeating(&CupsPrintersHandler::HandleGetEulaUrl, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "queryPrintServer", + base::BindRepeating(&CupsPrintersHandler::HandleQueryPrintServer, + base::Unretained(this))); } void CupsPrintersHandler::OnJavascriptAllowed() { @@ -513,8 +488,8 @@ void CupsPrintersHandler::HandleGetPrinterInfo(const base::ListValue* args) { if (printer_address.empty()) { // Run the failure callback. - OnAutoconfQueried(callback_id, PrinterQueryResult::UNKNOWN_FAILURE, "", "", - "", {}, false); + OnAutoconfQueried(callback_id, PrinterQueryResult::UNKNOWN_FAILURE, + printing::PrinterStatus(), "", "", "", {}, false); return; } @@ -541,6 +516,7 @@ void CupsPrintersHandler::OnAutoconfQueriedDiscovered( const std::string& callback_id, Printer printer, PrinterQueryResult result, + const printing::PrinterStatus& printer_status, const std::string& make, const std::string& model, const std::string& make_and_model, @@ -587,6 +563,7 @@ void CupsPrintersHandler::OnAutoconfQueriedDiscovered( void CupsPrintersHandler::OnAutoconfQueried( const std::string& callback_id, PrinterQueryResult result, + const printing::PrinterStatus& printer_status, const std::string& make, const std::string& model, const std::string& make_and_model, @@ -736,7 +713,7 @@ void CupsPrintersHandler::AddOrReconfigurePrinter(const base::ListValue* args, // Check if the printer already has a valid ppd_reference. Printer::PpdReference ppd_ref = GetPpdReference(printer_dict); - if (IsFilledPpdReference(ppd_ref)) { + if (ppd_ref.IsFilled()) { *printer->mutable_ppd_reference() = ppd_ref; } else if (!printer_ppd_path.empty()) { GURL tmp = net::FilePathToFileURL(base::FilePath(printer_ppd_path)); @@ -1003,9 +980,8 @@ void CupsPrintersHandler::FileSelected(const base::FilePath& path, // VerifyPpdContents() in order to determine whether the file appears to be a // PPD file. The task's priority is USER_BLOCKING because the this task // updates the UI as a result of a direct user action. - base::PostTaskAndReplyWithResult( - FROM_HERE, - {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_BLOCKING}, + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, base::BindOnce(&ReadFileToStringWithMaxSize, path, kPpdMaxLineLength), base::BindOnce(&CupsPrintersHandler::VerifyPpdContents, weak_factory_.GetWeakPtr(), path)); @@ -1239,7 +1215,7 @@ void CupsPrintersHandler::OnGetEulaUrl(const std::string& callback_id, return; } - GURL eula_url(chrome::kChromeUIOSCreditsURL + license); + GURL eula_url = PrinterConfigurer::GeneratePrinterEulaUrl(license); ResolveJavascriptCallback( base::Value(callback_id), eula_url.is_valid() ? base::Value(eula_url.spec()) : base::Value()); @@ -1283,27 +1259,45 @@ void CupsPrintersHandler::HandleQueryPrintServer(const base::ListValue* args) { CHECK(args->GetString(0, &callback_id)); CHECK(args->GetString(1, &server_url)); - GURL server_gurl; - if (!ConvertToGURL(server_url, &server_gurl)) { + base::Optional<GURL> converted_server_url = + GenerateServerPrinterUrlWithValidScheme(server_url); + if (!converted_server_url) { RejectJavascriptCallback( base::Value(callback_id), base::Value(PrintServerQueryResult::kIncorrectUrl)); return; } + // Use fallback only if HasValidServerPrinterScheme is false. + QueryPrintServer(callback_id, converted_server_url.value(), + !HasValidServerPrinterScheme(GURL(server_url))); +} + +void CupsPrintersHandler::QueryPrintServer(const std::string& callback_id, + const GURL& server_url, + bool should_fallback) { server_printers_fetcher_ = std::make_unique<ServerPrintersFetcher>( - server_gurl, "(from user)", + server_url, "(from user)", base::BindRepeating(&CupsPrintersHandler::OnQueryPrintServerCompleted, - weak_factory_.GetWeakPtr(), callback_id)); + weak_factory_.GetWeakPtr(), callback_id, + should_fallback)); } void CupsPrintersHandler::OnQueryPrintServerCompleted( const std::string& callback_id, + bool should_fallback, const ServerPrintersFetcher* sender, const GURL& server_url, std::vector<PrinterDetector::DetectedPrinter>&& returned_printers) { const PrintServerQueryResult result = sender->GetLastError(); if (result != PrintServerQueryResult::kNoErrors) { + if (should_fallback) { + // Apply the fallback query. + QueryPrintServer(callback_id, GenerateHttpCupsServerUrl(server_url), + /*should_fallback=*/false); + return; + } + RejectJavascriptCallback(base::Value(callback_id), base::Value(result)); return; } @@ -1313,9 +1307,10 @@ void CupsPrintersHandler::OnQueryPrintServerCompleted( printers_manager_->GetPrinters(PrinterClass::kSaved); std::set<GURL> known_printers; for (const Printer& printer : saved_printers) { - GURL gurl; - if (ConvertToGURL(printer.uri(), &gurl)) - known_printers.insert(gurl); + base::Optional<GURL> gurl = + GenerateServerPrinterUrlWithValidScheme(printer.uri()); + if (gurl) + known_printers.insert(gurl.value()); } // Built final list of printers and a list of current names. If "current name" @@ -1325,11 +1320,10 @@ void CupsPrintersHandler::OnQueryPrintServerCompleted( printers.reserve(returned_printers.size()); for (PrinterDetector::DetectedPrinter& printer : returned_printers) { printers.push_back(std::move(printer.printer)); - GURL printer_gurl; - if (ConvertToGURL(printers.back().uri(), &printer_gurl)) { - if (known_printers.count(printer_gurl)) - printers.pop_back(); - } + base::Optional<GURL> printer_gurl = + GenerateServerPrinterUrlWithValidScheme(printers.back().uri()); + if (printer_gurl && known_printers.count(printer_gurl.value())) + printers.pop_back(); } // Delete fetcher object. diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h index 6e80a910a18..82de71cf6cc 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h @@ -18,7 +18,7 @@ #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chromeos/printing/ppd_provider.h" #include "chromeos/printing/printer_configuration.h" -#include "printing/printer_query_result_chromeos.h" +#include "printing/printer_query_result.h" #include "ui/shell_dialogs/select_file_dialog.h" namespace base { @@ -30,6 +30,10 @@ namespace local_discovery { class EndpointResolver; } // namespace local_discovery +namespace printing { +struct PrinterStatus; +} // namespace printing + class GURL; class Profile; @@ -77,14 +81,17 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler, void HandleGetPrinterInfo(const base::ListValue* args); // Handles the callback for HandleGetPrinterInfo. |callback_id| is the - // identifier to resolve the correct Promise. |success| indicates if the query - // was successful. |make| is the detected printer manufacturer. |model| is the + // identifier to resolve the correct Promise. |result| indicates if the query + // was successful. |printer_status| contains the current status of the + // printer. |make| is the detected printer manufacturer. |model| is the // detected model. |make_and_model| is the unparsed printer-make-and-model // string. |ipp_everywhere| indicates if configuration using the CUPS IPP - // Everywhere driver should be attempted. If |success| is false, the values of - // |make|, |model|, |make_and_model|, and |ipp_everywhere| are not specified. + // Everywhere driver should be attempted. If |result| is not SUCCESS, the + // values of |printer_status|, |make|, |model|, |make_and_model|, and + // |ipp_everywhere| are not specified. void OnAutoconfQueried(const std::string& callback_id, printing::PrinterQueryResult result, + const printing::PrinterStatus& printer_status, const std::string& make, const std::string& model, const std::string& make_and_model, @@ -96,6 +103,7 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler, const std::string& callback_id, Printer printer, printing::PrinterQueryResult result, + const printing::PrinterStatus& printer_status, const std::string& make, const std::string& model, const std::string& make_and_model, @@ -214,8 +222,14 @@ class CupsPrintersHandler : public ::settings::SettingsPageUIHandler, const net::IPEndPoint& endpoint); void HandleQueryPrintServer(const base::ListValue* args); + + void QueryPrintServer(const std::string& callback_id, + const GURL& server_url, + bool should_fallback); + void OnQueryPrintServerCompleted( const std::string& callback_id, + bool should_fallback, const ServerPrintersFetcher* sender, const GURL& server_url, std::vector<PrinterDetector::DetectedPrinter>&& returned_printers); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc index 59656e6e848..39ceb89c02a 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc @@ -168,8 +168,8 @@ void DateTimeHandler::HandleShowParentAccessForTimeZone( ash::LoginScreen::Get()->ShowParentAccessWidget( user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(), - base::BindRepeating(&DateTimeHandler::OnParentAccessValidation, - weak_ptr_factory_.GetWeakPtr()), + base::BindOnce(&DateTimeHandler::OnParentAccessValidation, + weak_ptr_factory_.GetWeakPtr()), ash::ParentAccessRequestReason::kChangeTimezone, false /* extra_dimmer */, base::Time()); } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc index ee16a539c7b..7195f59a799 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.cc @@ -6,7 +6,6 @@ #include "ash/public/cpp/keyboard_shortcut_viewer.h" #include "ash/public/cpp/tablet_mode.h" -#include "ash/public/mojom/constants.mojom.h" #include "base/bind.h" #include "base/command_line.h" #include "base/values.h" @@ -21,25 +20,33 @@ namespace { struct KeyboardsStateResult { bool has_internal_keyboard = false; - bool has_external_non_apple_keyboard = false; - bool has_apple_keyboard = false; + bool has_external_apple_keyboard = false; + bool has_external_chromeos_keyboard = false; + bool has_external_generic_keyboard = false; }; KeyboardsStateResult GetKeyboardsState() { KeyboardsStateResult result; for (const ui::InputDevice& keyboard : ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) { - result.has_internal_keyboard |= - (keyboard.type == ui::INPUT_DEVICE_INTERNAL); - - const ui::EventRewriterChromeOS::DeviceType type = - ui::EventRewriterChromeOS::GetDeviceType(keyboard); - if (type == ui::EventRewriterChromeOS::kDeviceAppleKeyboard) { - result.has_apple_keyboard = true; - } else if (type == - ui::EventRewriterChromeOS::kDeviceExternalNonAppleKeyboard || - type == ui::EventRewriterChromeOS::kDeviceExternalUnknown) { - result.has_external_non_apple_keyboard = true; + switch (ui::EventRewriterChromeOS::GetDeviceType(keyboard)) { + case ui::EventRewriterChromeOS::kDeviceInternalKeyboard: + result.has_internal_keyboard = true; + break; + case ui::EventRewriterChromeOS::kDeviceExternalAppleKeyboard: + result.has_external_apple_keyboard = true; + break; + case ui::EventRewriterChromeOS::kDeviceExternalChromeOsKeyboard: + result.has_external_chromeos_keyboard = true; + break; + case ui::EventRewriterChromeOS::kDeviceExternalGenericKeyboard: + case ui::EventRewriterChromeOS::kDeviceExternalUnknown: + result.has_external_generic_keyboard = true; + break; + case ui::EventRewriterChromeOS::kDeviceHotrodRemote: + case ui::EventRewriterChromeOS::kDeviceVirtualCoreKeyboard: + case ui::EventRewriterChromeOS::kDeviceUnknown: + break; } } @@ -131,8 +138,8 @@ void KeyboardHandler::UpdateShowKeys() { // kHasChromeOSKeyboard will be unset on Chromebooks that have standalone Caps // Lock keys. const KeyboardsStateResult keyboards_state = GetKeyboardsState(); - const bool has_caps_lock = keyboards_state.has_apple_keyboard || - keyboards_state.has_external_non_apple_keyboard || + const bool has_caps_lock = keyboards_state.has_external_apple_keyboard || + keyboards_state.has_external_generic_keyboard || !base::CommandLine::ForCurrentProcess()->HasSwitch( chromeos::switches::kHasChromeOSKeyboard); @@ -140,9 +147,10 @@ void KeyboardHandler::UpdateShowKeys() { keyboard_params.SetKey("showCapsLock", base::Value(has_caps_lock)); keyboard_params.SetKey( "showExternalMetaKey", - base::Value(keyboards_state.has_external_non_apple_keyboard)); - keyboard_params.SetKey("showAppleCommandKey", - base::Value(keyboards_state.has_apple_keyboard)); + base::Value(keyboards_state.has_external_generic_keyboard)); + keyboard_params.SetKey( + "showAppleCommandKey", + base::Value(keyboards_state.has_external_apple_keyboard)); keyboard_params.SetKey("hasInternalKeyboard", base::Value(keyboards_state.has_internal_keyboard)); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc index 8f4f6318a50..a558502222a 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler_unittest.cc @@ -15,6 +15,7 @@ #include "base/observer_list.h" #include "chromeos/constants/chromeos_switches.h" #include "content/public/test/test_web_ui.h" +#include "device/udev_linux/fake_udev_loader.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/devices/device_data_manager_test_api.h" #include "ui/events/devices/input_device.h" @@ -192,11 +193,46 @@ TEST_F(KeyboardHandlerTest, NonChromeOSKeyboard) { } TEST_F(KeyboardHandlerTest, ExternalKeyboard) { + auto fake_udev = std::make_unique<testing::FakeUdevLoader>(); + + // Standard internal keyboard on x86 device. + const ui::InputDevice internal_kbd( + 1, ui::INPUT_DEVICE_INTERNAL, "AT Translated Set 2 keyboard", "", + base::FilePath("/devices/platform/i8042/serio0/input/input1"), 1, 1, + 0xab41); + fake_udev->AddFakeDevice(internal_kbd.name, internal_kbd.sys_path.value(), {}, + {}); + // Generic external USB keyboard. + const ui::InputDevice external_generic_kbd( + 2, ui::INPUT_DEVICE_USB, "Logitech USB Keyboard", "", + base::FilePath("/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/" + "0003:046D:C31C.0007/" + "input/input2"), + 0x046d, 0xc31c, 0x0111); + fake_udev->AddFakeDevice(external_generic_kbd.name, + external_generic_kbd.sys_path.value(), {}, {}); + // Apple keyboard. + const ui::InputDevice external_apple_kbd( + 3, ui::INPUT_DEVICE_USB, "Apple Inc. Apple Keyboard", "", + base::FilePath("/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/" + "0003:05AC:026C.000A/input/input3"), + 0x05ac, 0x026c, 0x0111); + fake_udev->AddFakeDevice(external_apple_kbd.name, + external_apple_kbd.sys_path.value(), {}, {}); + // Chrome OS external USB keyboard. + const ui::InputDevice external_chromeos_kbd( + 4, ui::INPUT_DEVICE_USB, "LG USB Keyboard", "", + base::FilePath("/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.0/" + "0003:04CA:0082.000B/input/input4"), + 0x04ca, 0x0082, 0x0111); + fake_udev->AddFakeDevice(external_chromeos_kbd.name, + external_chromeos_kbd.sys_path.value(), {}, + {{"CROS_KEYBOARD_TOP_ROW_LAYOUT", "1"}}); + // An internal keyboard shouldn't change the defaults. base::CommandLine::ForCurrentProcess()->AppendSwitch( chromeos::switches::kHasChromeOSKeyboard); - device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{ - {1, ui::INPUT_DEVICE_INTERNAL, "internal keyboard"}}); + device_data_manager_test_api_.SetKeyboardDevices({internal_kbd}); handler_test_api_.Initialize(); EXPECT_TRUE(HasInternalSearchKey()); EXPECT_FALSE(HasCapsLock()); @@ -206,20 +242,28 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) { // Simulate an external keyboard being connected. We should assume there's a // Caps Lock and Meta keys now. - device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{ - {1, ui::INPUT_DEVICE_INTERNAL, "internal keyboard"}, - {2, ui::INPUT_DEVICE_USB, "external keyboard"}}); + device_data_manager_test_api_.SetKeyboardDevices( + std::vector<ui::InputDevice>{internal_kbd, external_generic_kbd}); EXPECT_TRUE(HasInternalSearchKey()); EXPECT_TRUE(HasCapsLock()); EXPECT_TRUE(HasExternalMetaKey()); EXPECT_FALSE(HasAppleCommandKey()); EXPECT_FALSE(HasAssistantKey()); + // However when connecting external ChromeOS-branded keyboard, we should not + // see neither CapsLock not meta keys. + device_data_manager_test_api_.SetKeyboardDevices( + std::vector<ui::InputDevice>{internal_kbd, external_chromeos_kbd}); + EXPECT_TRUE(HasInternalSearchKey()); + EXPECT_FALSE(HasCapsLock()); + EXPECT_FALSE(HasExternalMetaKey()); + EXPECT_FALSE(HasAppleCommandKey()); + EXPECT_FALSE(HasAssistantKey()); + // Simulate an external Apple keyboard being connected. Now users can remap // the command key. - device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{ - {1, ui::INPUT_DEVICE_INTERNAL, "internal keyboard"}, - {3, ui::INPUT_DEVICE_USB, "Apple Inc. Apple Keyboard"}}); + device_data_manager_test_api_.SetKeyboardDevices( + std::vector<ui::InputDevice>{internal_kbd, external_apple_kbd}); EXPECT_TRUE(HasInternalSearchKey()); EXPECT_TRUE(HasCapsLock()); EXPECT_FALSE(HasExternalMetaKey()); @@ -228,9 +272,8 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) { // Simulate two external keyboards (Apple and non-Apple) are connected at the // same time. - device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{ - {2, ui::INPUT_DEVICE_USB, "external keyboard"}, - {3, ui::INPUT_DEVICE_USB, "Apple Inc. Apple Keyboard"}}); + device_data_manager_test_api_.SetKeyboardDevices( + std::vector<ui::InputDevice>{external_generic_kbd, external_apple_kbd}); EXPECT_FALSE(HasInternalSearchKey()); EXPECT_TRUE(HasCapsLock()); EXPECT_TRUE(HasExternalMetaKey()); @@ -242,7 +285,8 @@ TEST_F(KeyboardHandlerTest, ExternalKeyboard) { // should show the capslock and external meta remapping. // https://crbug.com/834594. device_data_manager_test_api_.SetKeyboardDevices(std::vector<ui::InputDevice>{ - {4, ui::INPUT_DEVICE_USB, "Topre Corporation Realforce 87"}}); + {5, ui::INPUT_DEVICE_USB, "Topre Corporation Realforce 87", "", + external_generic_kbd.sys_path, 0x046d, 0xc31c, 0x0111}}); EXPECT_FALSE(HasInternalSearchKey()); EXPECT_TRUE(HasCapsLock()); EXPECT_TRUE(HasExternalMetaKey()); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc index 1906a330cd5..a5c66d6f18f 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc @@ -79,11 +79,34 @@ int PowerSourceToDisplayId( } // namespace +PowerHandler::IdleBehaviorInfo::IdleBehaviorInfo() = default; +PowerHandler::IdleBehaviorInfo::~IdleBehaviorInfo() = default; + +PowerHandler::IdleBehaviorInfo::IdleBehaviorInfo( + const std::set<PowerHandler::IdleBehavior>& possible_behaviors, + const PowerHandler::IdleBehavior& current_behavior, + const bool is_managed) + : possible_behaviors(possible_behaviors), + current_behavior(current_behavior), + is_managed(is_managed) {} + +PowerHandler::IdleBehaviorInfo::IdleBehaviorInfo(const IdleBehaviorInfo& o) = + default; + const char PowerHandler::kPowerManagementSettingsChangedName[] = "power-management-settings-changed"; -const char PowerHandler::kIdleBehaviorKey[] = "idleBehavior"; -const char PowerHandler::kIdleControlledKey[] = "idleControlled"; + +const char PowerHandler::kPossibleAcIdleBehaviorsKey[] = + "possibleAcIdleBehaviors"; +const char PowerHandler::kPossibleBatteryIdleBehaviorsKey[] = + "possibleBatteryIdleBehaviors"; +const char PowerHandler::kCurrentAcIdleBehaviorKey[] = "currentAcIdleBehavior"; +const char PowerHandler::kCurrentBatteryIdleBehaviorKey[] = + "currentBatteryIdleBehavior"; const char PowerHandler::kLidClosedBehaviorKey[] = "lidClosedBehavior"; +const char PowerHandler::kAcIdleManagedKey[] = "acIdleManaged"; +const char PowerHandler::kBatteryIdleManagedKey[] = "batteryIdleManaged"; + const char PowerHandler::kLidClosedControlledKey[] = "lidClosedControlled"; const char PowerHandler::kHasLidKey[] = "hasLid"; @@ -96,9 +119,11 @@ void PowerHandler::TestAPI::RequestPowerManagementSettings() { handler_->HandleRequestPowerManagementSettings(&args); } -void PowerHandler::TestAPI::SetIdleBehavior(IdleBehavior behavior) { +void PowerHandler::TestAPI::SetIdleBehavior(IdleBehavior behavior, + bool when_on_ac) { base::ListValue args; args.AppendInteger(static_cast<int>(behavior)); + args.AppendBoolean(when_on_ac); handler_->HandleSetIdleBehavior(&args); } @@ -205,49 +230,48 @@ void PowerHandler::HandleSetIdleBehavior(const base::ListValue* args) { AllowJavascript(); int value = 0; + bool when_on_ac = true; CHECK(args->GetInteger(0, &value)); + CHECK(args->GetBoolean(1, &when_on_ac)); + + const char* idle_pref = when_on_ac ? ash::prefs::kPowerAcIdleAction + : ash::prefs::kPowerBatteryIdleAction; + const char* screen_dim_delay_pref = + when_on_ac ? ash::prefs::kPowerAcScreenDimDelayMs + : ash::prefs::kPowerBatteryScreenDimDelayMs; + const char* screen_off_delay_pref = + when_on_ac ? ash::prefs::kPowerAcScreenOffDelayMs + : ash::prefs::kPowerBatteryScreenOffDelayMs; + const char* screen_lock_delay_pref = + when_on_ac ? ash::prefs::kPowerAcScreenLockDelayMs + : ash::prefs::kPowerBatteryScreenLockDelayMs; + switch (static_cast<IdleBehavior>(value)) { case IdleBehavior::DISPLAY_OFF_SLEEP: - // The default behavior is to turn the display off and sleep. Clear the - // prefs so we use the default delays. - prefs_->ClearPref(ash::prefs::kPowerAcIdleAction); - prefs_->ClearPref(ash::prefs::kPowerAcScreenDimDelayMs); - prefs_->ClearPref(ash::prefs::kPowerAcScreenOffDelayMs); - prefs_->ClearPref(ash::prefs::kPowerAcScreenLockDelayMs); - prefs_->ClearPref(ash::prefs::kPowerBatteryIdleAction); - prefs_->ClearPref(ash::prefs::kPowerBatteryScreenDimDelayMs); - prefs_->ClearPref(ash::prefs::kPowerBatteryScreenOffDelayMs); - prefs_->ClearPref(ash::prefs::kPowerBatteryScreenLockDelayMs); + // The default behavior is to turn the display off and sleep. + // Clear the prefs so we use the default delays. + prefs_->ClearPref(idle_pref); + prefs_->ClearPref(screen_dim_delay_pref); + prefs_->ClearPref(screen_off_delay_pref); + prefs_->ClearPref(screen_lock_delay_pref); break; case IdleBehavior::DISPLAY_OFF: - // Override idle actions to keep the system awake, but use the default - // screen delays. - prefs_->SetInteger(ash::prefs::kPowerAcIdleAction, - PowerPolicyController::ACTION_DO_NOTHING); - prefs_->ClearPref(ash::prefs::kPowerAcScreenDimDelayMs); - prefs_->ClearPref(ash::prefs::kPowerAcScreenOffDelayMs); - prefs_->ClearPref(ash::prefs::kPowerAcScreenLockDelayMs); - prefs_->SetInteger(ash::prefs::kPowerBatteryIdleAction, - PowerPolicyController::ACTION_DO_NOTHING); - prefs_->ClearPref(ash::prefs::kPowerBatteryScreenDimDelayMs); - prefs_->ClearPref(ash::prefs::kPowerBatteryScreenOffDelayMs); - prefs_->ClearPref(ash::prefs::kPowerBatteryScreenLockDelayMs); + // Override idle actions to keep the system awake, but use the + // default screen delays. + prefs_->SetInteger(idle_pref, PowerPolicyController::ACTION_DO_NOTHING); + prefs_->ClearPref(screen_dim_delay_pref); + prefs_->ClearPref(screen_off_delay_pref); + prefs_->ClearPref(screen_lock_delay_pref); break; case IdleBehavior::DISPLAY_ON: - // Override idle actions and set screen delays to 0 in order to disable - // them (i.e. keep the screen on). - prefs_->SetInteger(ash::prefs::kPowerAcIdleAction, - PowerPolicyController::ACTION_DO_NOTHING); - prefs_->SetInteger(ash::prefs::kPowerAcScreenDimDelayMs, 0); - prefs_->SetInteger(ash::prefs::kPowerAcScreenOffDelayMs, 0); - prefs_->SetInteger(ash::prefs::kPowerAcScreenLockDelayMs, 0); - prefs_->SetInteger(ash::prefs::kPowerBatteryIdleAction, - PowerPolicyController::ACTION_DO_NOTHING); - prefs_->SetInteger(ash::prefs::kPowerBatteryScreenDimDelayMs, 0); - prefs_->SetInteger(ash::prefs::kPowerBatteryScreenOffDelayMs, 0); - prefs_->SetInteger(ash::prefs::kPowerBatteryScreenLockDelayMs, 0); + // Override idle actions and set screen delays to 0 in order to + // disable them (i.e. keep the screen on). + prefs_->SetInteger(idle_pref, PowerPolicyController::ACTION_DO_NOTHING); + prefs_->SetInteger(screen_dim_delay_pref, 0); + prefs_->SetInteger(screen_off_delay_pref, 0); + prefs_->SetInteger(screen_lock_delay_pref, 0); break; - default: + case IdleBehavior::OTHER: NOTREACHED() << "Invalid idle behavior " << value; } } @@ -336,32 +360,10 @@ void PowerHandler::SendPowerSources() { } void PowerHandler::SendPowerManagementSettings(bool force) { - // Infer the idle behavior based on the idle action (determining whether we'll - // sleep eventually or not) and the AC screen-off delay. Policy can request - // more-nuanced combinations of AC/battery actions and delays, but we wouldn't - // be able to display something meaningful in the UI in those cases anyway. - const PowerPolicyController::Action idle_action = - static_cast<PowerPolicyController::Action>( - prefs_->GetInteger(ash::prefs::kPowerAcIdleAction)); - IdleBehavior idle_behavior = IdleBehavior::OTHER; - if (idle_action == PowerPolicyController::ACTION_SUSPEND) { - idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP; - } else if (idle_action == PowerPolicyController::ACTION_DO_NOTHING) { - idle_behavior = - (prefs_->GetInteger(ash::prefs::kPowerAcScreenOffDelayMs) > 0 - ? IdleBehavior::DISPLAY_OFF - : IdleBehavior::DISPLAY_ON); - } - - const bool idle_controlled = - prefs_->IsManagedPreference(ash::prefs::kPowerAcIdleAction) || - prefs_->IsManagedPreference(ash::prefs::kPowerAcScreenDimDelayMs) || - prefs_->IsManagedPreference(ash::prefs::kPowerAcScreenOffDelayMs) || - prefs_->IsManagedPreference(ash::prefs::kPowerAcScreenLockDelayMs) || - prefs_->IsManagedPreference(ash::prefs::kPowerBatteryIdleAction) || - prefs_->IsManagedPreference(ash::prefs::kPowerBatteryScreenDimDelayMs) || - prefs_->IsManagedPreference(ash::prefs::kPowerBatteryScreenOffDelayMs) || - prefs_->IsManagedPreference(ash::prefs::kPowerBatteryScreenLockDelayMs); + const PowerHandler::IdleBehaviorInfo ac_idle_info = + GetAllowedIdleBehaviors(PowerSource::kAc); + const PowerHandler::IdleBehaviorInfo battery_idle_info = + GetAllowedIdleBehaviors(PowerSource::kBattery); const PowerPolicyController::Action lid_closed_behavior = static_cast<PowerPolicyController::Action>( @@ -371,23 +373,37 @@ void PowerHandler::SendPowerManagementSettings(bool force) { const bool has_lid = lid_state_ != PowerManagerClient::LidState::NOT_PRESENT; // Don't notify the UI if nothing changed. - if (!force && idle_behavior == last_idle_behavior_ && - idle_controlled == last_idle_controlled_ && + if (!force && ac_idle_info == last_ac_idle_info_ && + battery_idle_info == last_battery_idle_info_ && lid_closed_behavior == last_lid_closed_behavior_ && lid_closed_controlled == last_lid_closed_controlled_ && - has_lid == last_has_lid_) + has_lid == last_has_lid_) { return; + } base::DictionaryValue dict; - dict.SetInteger(kIdleBehaviorKey, static_cast<int>(idle_behavior)); - dict.SetBoolean(kIdleControlledKey, idle_controlled); + base::Value* list = dict.SetKey(kPossibleAcIdleBehaviorsKey, + base::Value(base::Value::Type::LIST)); + for (auto idle_behavior : ac_idle_info.possible_behaviors) + list->Append(static_cast<int>(idle_behavior)); + + list = dict.SetKey(kPossibleBatteryIdleBehaviorsKey, + base::Value(base::Value::Type::LIST)); + for (auto idle_behavior : battery_idle_info.possible_behaviors) + list->Append(static_cast<int>(idle_behavior)); + dict.SetInteger(kCurrentAcIdleBehaviorKey, + static_cast<int>(ac_idle_info.current_behavior)); + dict.SetInteger(kCurrentBatteryIdleBehaviorKey, + static_cast<int>(battery_idle_info.current_behavior)); dict.SetInteger(kLidClosedBehaviorKey, lid_closed_behavior); + dict.SetBoolean(kAcIdleManagedKey, ac_idle_info.is_managed); + dict.SetBoolean(kBatteryIdleManagedKey, battery_idle_info.is_managed); dict.SetBoolean(kLidClosedControlledKey, lid_closed_controlled); dict.SetBoolean(kHasLidKey, has_lid); FireWebUIListener(kPowerManagementSettingsChangedName, dict); - last_idle_behavior_ = idle_behavior; - last_idle_controlled_ = idle_controlled; + last_ac_idle_info_ = ac_idle_info; + last_battery_idle_info_ = battery_idle_info; last_lid_closed_behavior_ = lid_closed_behavior; last_lid_closed_controlled_ = lid_closed_controlled; last_has_lid_ = has_lid; @@ -401,5 +417,159 @@ void PowerHandler::OnGotSwitchStates( SendPowerManagementSettings(false /* force */); } +PowerHandler::IdleBehaviorInfo PowerHandler::GetAllowedIdleBehaviors( + PowerSource power_source) { + const char* idle_pref = power_source == PowerSource::kAc + ? ash::prefs::kPowerAcIdleAction + : ash::prefs::kPowerBatteryIdleAction; + const char* screen_off_delay_pref = + power_source == PowerSource::kAc + ? ash::prefs::kPowerAcScreenOffDelayMs + : ash::prefs::kPowerBatteryScreenOffDelayMs; + + std::set<IdleBehavior> possible_behaviors; + IdleBehavior current_idle_behavior; + + // If idle action is managed and set to suspend, only possible idle + // behaviour is sleep with display off. + if (prefs_->IsManagedPreference(idle_pref) && + prefs_->GetInteger(idle_pref) == PowerPolicyController::ACTION_SUSPEND) { + current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP; + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF_SLEEP); + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); + } + + // If idle action is managed and set to SHUT_DOWN/STOP_SESSION, only + // possible idle behaviour is other. + if (prefs_->IsManagedPreference(idle_pref) && + (prefs_->GetInteger(idle_pref) == + PowerPolicyController::ACTION_STOP_SESSION || + prefs_->GetInteger(idle_pref) == + PowerPolicyController::ACTION_SHUT_DOWN)) { + current_idle_behavior = IdleBehavior::OTHER; + possible_behaviors.insert(IdleBehavior::OTHER); + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); + } + + // Note that after this point |idle_pref| should either be: + // 1. Not managed. + // 2. Or managed and set to + // PowerPolicyController::ACTION_DO_NOTHING. + DCHECK(!prefs_->IsManagedPreference(idle_pref) || + (prefs_->GetInteger(idle_pref) == + PowerPolicyController::ACTION_DO_NOTHING)); + + // If screen off delay is managed and set to a value greater than 0 + // and + // 1. If idle action is managed and set to DO_NOTHING, only + // possible idle behavior is DISPLAY_OFF. + // 2. If idle action is not managed then possible idle options + // are DiSPLAY_OFF and DISPLAY_OFF_SLEEP + if (prefs_->IsManagedPreference(screen_off_delay_pref) && + prefs_->GetInteger(screen_off_delay_pref) > 0) { + if (prefs_->IsManagedPreference(idle_pref) && + prefs_->GetInteger(idle_pref) == + PowerPolicyController::ACTION_DO_NOTHING) { + current_idle_behavior = IdleBehavior::DISPLAY_OFF; + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF); + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); + } + + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF); + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF_SLEEP); + // Set the current default option based on the current idle action. + const PowerPolicyController::Action idle_action = + static_cast<PowerPolicyController::Action>( + prefs_->GetInteger(idle_pref)); + + if (idle_action == PowerPolicyController::ACTION_SUSPEND) + current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP; + else + current_idle_behavior = IdleBehavior::DISPLAY_OFF; + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); + } + + // If idle action is managed and set to DO_NOTHING, and + // 1. If screen off delay is also managed (and set to 0), only + // possible idle + // action is DISPLAY_ON. + // 2. If AC screen off delay is not managed, possible idle actions + // are DISPLAY_ON && DISPLAY_OFF. + if (prefs_->IsManagedPreference(idle_pref) && + prefs_->GetInteger(idle_pref) == + PowerPolicyController::ACTION_DO_NOTHING) { + if (prefs_->IsManagedPreference(screen_off_delay_pref)) { + // Note that we reach here only when screen off delays are + // set by enterprise policy to 0 to prevent display from turning + // off. + DCHECK(prefs_->GetInteger(screen_off_delay_pref) == 0); + current_idle_behavior = IdleBehavior::DISPLAY_ON; + possible_behaviors.insert(IdleBehavior::DISPLAY_ON); + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); + } + possible_behaviors.insert(IdleBehavior::DISPLAY_ON); + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF); + // Set the current default option based on the current screen off + // delay. + current_idle_behavior = prefs_->GetInteger(screen_off_delay_pref) > 0 + ? IdleBehavior::DISPLAY_OFF + : IdleBehavior::DISPLAY_ON; + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); + } + + // Looks like we did not find enterprise policy restricitng the idle + // options. So add all three idle options to what user can select + // from. + possible_behaviors.insert(IdleBehavior::DISPLAY_ON); + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF); + possible_behaviors.insert(IdleBehavior::DISPLAY_OFF_SLEEP); + + // Infer the idle behavior based on the current idle action + // (determining whether we'll sleep eventually or not) and the AC + // screen-off delay. + const PowerPolicyController::Action idle_action = + static_cast<PowerPolicyController::Action>(prefs_->GetInteger(idle_pref)); + + if (idle_action == PowerPolicyController::ACTION_SUSPEND) { + current_idle_behavior = IdleBehavior::DISPLAY_OFF_SLEEP; + } else if (idle_action == PowerPolicyController::ACTION_DO_NOTHING) { + current_idle_behavior = (prefs_->GetInteger(screen_off_delay_pref) > 0 + ? IdleBehavior::DISPLAY_OFF + : IdleBehavior::DISPLAY_ON); + } else { + current_idle_behavior = IdleBehavior::OTHER; + possible_behaviors.insert(IdleBehavior::OTHER); + } + + return IdleBehaviorInfo(possible_behaviors, current_idle_behavior, + IsIdleManaged(power_source)); +} + +bool PowerHandler::IsIdleManaged(PowerSource power_source) { + switch (power_source) { + case PowerSource::kAc: + return prefs_->IsManagedPreference(ash::prefs::kPowerAcIdleAction) || + prefs_->IsManagedPreference( + ash::prefs::kPowerAcScreenDimDelayMs) || + prefs_->IsManagedPreference( + ash::prefs::kPowerAcScreenOffDelayMs) || + prefs_->IsManagedPreference(ash::prefs::kPowerAcScreenLockDelayMs); + case PowerSource::kBattery: + return prefs_->IsManagedPreference(ash::prefs::kPowerBatteryIdleAction) || + prefs_->IsManagedPreference( + ash::prefs::kPowerBatteryScreenDimDelayMs) || + prefs_->IsManagedPreference( + ash::prefs::kPowerBatteryScreenOffDelayMs) || + prefs_->IsManagedPreference( + ash::prefs::kPowerBatteryScreenLockDelayMs); + } +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h index d55a3944bd5..1d125b0295a 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_POWER_HANDLER_H_ #include <memory> +#include <set> #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -22,7 +23,7 @@ class PrefService; namespace base { class ListValue; class TimeTicks; -} +} // namespace base namespace chromeos { namespace settings { @@ -43,8 +44,12 @@ class PowerHandler : public ::settings::SettingsPageUIHandler, // WebUI message name and dictionary keys. Shared with tests. static const char kPowerManagementSettingsChangedName[]; - static const char kIdleBehaviorKey[]; - static const char kIdleControlledKey[]; + static const char kPossibleAcIdleBehaviorsKey[]; + static const char kPossibleBatteryIdleBehaviorsKey[]; + static const char kAcIdleManagedKey[]; + static const char kBatteryIdleManagedKey[]; + static const char kCurrentAcIdleBehaviorKey[]; + static const char kCurrentBatteryIdleBehaviorKey[]; static const char kLidClosedBehaviorKey[]; static const char kLidClosedControlledKey[]; static const char kHasLidKey[]; @@ -56,7 +61,9 @@ class PowerHandler : public ::settings::SettingsPageUIHandler, ~TestAPI(); void RequestPowerManagementSettings(); - void SetIdleBehavior(IdleBehavior behavior); + // Sets AC idle behavior to |behavior| if |when_on_ac| is true. Otherwise + // sets battery idle behavior to |behavior|. + void SetIdleBehavior(IdleBehavior behavior, bool when_on_ac); void SetLidClosedBehavior(PowerPolicyController::Action behavior); private: @@ -80,6 +87,33 @@ class PowerHandler : public ::settings::SettingsPageUIHandler, const base::TimeTicks& timestamp) override; private: + enum class PowerSource { kAc, kBattery }; + + // Struct holding possible idle behaviors and the current behavior while + // charging/when on battery. + struct IdleBehaviorInfo { + IdleBehaviorInfo(); + IdleBehaviorInfo(const std::set<IdleBehavior>& possible_behaviors, + const IdleBehavior& current_behavior, + const bool is_managed); + + IdleBehaviorInfo(const IdleBehaviorInfo& o); + ~IdleBehaviorInfo(); + + bool operator==(const IdleBehaviorInfo& o) const { + return (possible_behaviors == o.possible_behaviors && + current_behavior == o.current_behavior && + is_managed == o.is_managed); + } + + // All possible idle behaviors. + std::set<IdleBehavior> possible_behaviors; + // Current idle behavior. + IdleBehavior current_behavior = IdleBehavior::DISPLAY_OFF_SLEEP; + // Whether enterpise policy manages idle behavior. + bool is_managed = false; + }; + // Handler to request updating the power status. void HandleUpdatePowerStatus(const base::ListValue* args); @@ -109,7 +143,16 @@ class PowerHandler : public ::settings::SettingsPageUIHandler, void OnGotSwitchStates( base::Optional<PowerManagerClient::SwitchStates> result); - PrefService* prefs_; // Not owned. + // Returns all possible idle behaviors (that a user can choose from) and + // current idle behavior based on enterprise policy and other factors when on + // |power_source|. + IdleBehaviorInfo GetAllowedIdleBehaviors(PowerSource power_source); + + // Returns true if the enterprise policy enforces any settings that can impact + // the idle behavior of the device when on |power_source|. + bool IsIdleManaged(PowerSource power_source); + + PrefService* const prefs_; // Used to watch power management prefs for changes so the UI can be notified. std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_; @@ -123,10 +166,10 @@ class PowerHandler : public ::settings::SettingsPageUIHandler, // Last values sent by SendPowerManagementSettings(), cached here so // SendPowerManagementSettings() can avoid spamming the UI after this class // changes multiple prefs at once. - IdleBehavior last_idle_behavior_ = IdleBehavior::DISPLAY_OFF_SLEEP; + IdleBehaviorInfo last_ac_idle_info_; + IdleBehaviorInfo last_battery_idle_info_; PowerPolicyController::Action last_lid_closed_behavior_ = PowerPolicyController::ACTION_SUSPEND; - bool last_idle_controlled_ = false; bool last_lid_closed_controlled_ = false; bool last_has_lid_ = true; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc index c27e55cb6d4..c928ccd2d75 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_power_handler_browsertest.cc @@ -2,9 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h" - #include <memory> +#include <set> #include <utility> #include "ash/public/cpp/ash_pref_names.h" @@ -13,6 +12,7 @@ #include "base/run_loop.h" #include "base/values.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h" #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_policy_controller.h" @@ -24,8 +24,8 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using testing::Return; using testing::_; +using testing::Return; namespace chromeos { namespace settings { @@ -48,6 +48,34 @@ class TestPowerHandler : public PowerHandler { class PowerHandlerTest : public InProcessBrowserTest { protected: + struct DevicePowerSettings { + // Initialize with initial settings. + DevicePowerSettings() { + possible_ac_behaviors.insert( + PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP); + possible_ac_behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_OFF); + possible_ac_behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_ON); + possible_battery_behaviors.insert( + PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP); + possible_battery_behaviors.insert( + PowerHandler::IdleBehavior::DISPLAY_OFF); + possible_battery_behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_ON); + } + + std::set<PowerHandler::IdleBehavior> possible_ac_behaviors; + std::set<PowerHandler::IdleBehavior> possible_battery_behaviors; + PowerHandler::IdleBehavior current_ac_behavior = + PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP; + PowerHandler::IdleBehavior current_battery_behavior = + PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP; + bool ac_idle_managed = false; + bool battery_idle_managed = false; + PowerPolicyController::Action lid_closed_behavior = + PowerPolicyController::ACTION_SUSPEND; + bool lid_closed_controlled = false; + bool has_lid = true; + }; + PowerHandlerTest() = default; ~PowerHandlerTest() override = default; @@ -99,23 +127,32 @@ class PowerHandlerTest : public InProcessBrowserTest { return std::string(); } - // Returns a string for the given settings that can be compared against the - // output of GetLastSettingsChangedMessage(). - std::string CreateSettingsChangedString( - PowerHandler::IdleBehavior idle_behavior, - bool idle_controlled, - PowerPolicyController::Action lid_closed_behavior, - bool lid_closed_controlled, - bool has_lid) { + // Returns a string for the given |settings|. Used to verify expected + // settings are sent to the UI. + std::string ToString(const DevicePowerSettings& settings) { base::DictionaryValue dict; - dict.SetInteger(PowerHandler::kIdleBehaviorKey, - static_cast<int>(idle_behavior)); - dict.SetBoolean(PowerHandler::kIdleControlledKey, idle_controlled); - dict.SetInteger(PowerHandler::kLidClosedBehaviorKey, lid_closed_behavior); + base::Value* list = dict.SetKey(PowerHandler::kPossibleAcIdleBehaviorsKey, + base::Value(base::Value::Type::LIST)); + for (auto idle_behavior : settings.possible_ac_behaviors) + list->Append(static_cast<int>(idle_behavior)); + + list = dict.SetKey(PowerHandler::kPossibleBatteryIdleBehaviorsKey, + base::Value(base::Value::Type::LIST)); + for (auto idle_behavior : settings.possible_battery_behaviors) + list->Append(static_cast<int>(idle_behavior)); + + dict.SetInteger(PowerHandler::kCurrentAcIdleBehaviorKey, + static_cast<int>(settings.current_ac_behavior)); + dict.SetInteger(PowerHandler::kCurrentBatteryIdleBehaviorKey, + static_cast<int>(settings.current_battery_behavior)); + dict.SetBoolean(PowerHandler::kAcIdleManagedKey, settings.ac_idle_managed); + dict.SetBoolean(PowerHandler::kBatteryIdleManagedKey, + settings.battery_idle_managed); + dict.SetInteger(PowerHandler::kLidClosedBehaviorKey, + settings.lid_closed_behavior); dict.SetBoolean(PowerHandler::kLidClosedControlledKey, - lid_closed_controlled); - dict.SetBoolean(PowerHandler::kHasLidKey, has_lid); - + settings.lid_closed_controlled); + dict.SetBoolean(PowerHandler::kHasLidKey, settings.has_lid); std::string out; EXPECT_TRUE(base::JSONWriter::Write(dict, &out)); return out; @@ -129,6 +166,12 @@ class PowerHandlerTest : public InProcessBrowserTest { return GetPrefs()->GetInteger(name); } + // Trigger power pref managed change. + void UpdateChromePolicy(policy::PolicyMap* policy_map) { + provider_.UpdateChromePolicy(*policy_map); + base::RunLoop().RunUntilIdle(); + } + // Sets a policy update which will cause power pref managed change. void SetPolicyForPolicyKey(policy::PolicyMap* policy_map, const std::string& policy_key, @@ -136,8 +179,7 @@ class PowerHandlerTest : public InProcessBrowserTest { policy_map->Set(policy_key, policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, std::move(value), nullptr); - provider_.UpdateChromePolicy(*policy_map); - base::RunLoop().RunUntilIdle(); + UpdateChromePolicy(policy_map); } std::unique_ptr<TestPowerHandler> handler_; @@ -154,155 +196,215 @@ class PowerHandlerTest : public InProcessBrowserTest { // Verifies that settings are sent to WebUI when requested. IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SendInitialSettings) { test_api_->RequestPowerManagementSettings(); - EXPECT_EQ( - CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - false /* idle_controlled */, PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + // Initialized to initial settings. + DevicePowerSettings settings; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); } // Verifies that WebUI receives updated settings when the lid state changes. IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SendSettingsForLidStateChanges) { chromeos::FakePowerManagerClient::Get()->SetLidState( PowerManagerClient::LidState::NOT_PRESENT, base::TimeTicks()); - EXPECT_EQ( - CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - false /* idle_controlled */, PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, false /* has_lid */), - GetLastSettingsChangedMessage()); + + DevicePowerSettings settings; + settings.has_lid = false; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); chromeos::FakePowerManagerClient::Get()->SetLidState( PowerManagerClient::LidState::OPEN, base::TimeTicks()); - EXPECT_EQ( - CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - false /* idle_controlled */, PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + settings.has_lid = true; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); } -// Verifies that when various prefs are controlled, the corresponding settings -// are reported as controlled to WebUI. +// Verifies that when various prefs are controlled, the corresponding +// settings are reported as controlled/managed to WebUI. IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SendSettingsForControlledPrefs) { policy::PolicyMap policy_map; - // Making an arbitrary delay pref managed should result in the idle setting - // being reported as controlled. + // Making an arbitrary AC delay pref managed should result in the AC idle + // setting being reported as managed. SetPolicyForPolicyKey(&policy_map, policy::key::kScreenDimDelayAC, std::make_unique<base::Value>(10000)); - EXPECT_EQ( - CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - true /* idle_controlled */, PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + DevicePowerSettings settings; + settings.ac_idle_managed = true; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); + + // Ditto for battery delay pref managed. + SetPolicyForPolicyKey(&policy_map, policy::key::kScreenDimDelayBattery, + std::make_unique<base::Value>(10000)); + settings.battery_idle_managed = true; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); // Ditto for making the lid action pref managed. SetPolicyForPolicyKey( &policy_map, policy::key::kLidCloseAction, std::make_unique<base::Value>(PowerPolicyController::ACTION_SUSPEND)); - EXPECT_EQ( - CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - true /* idle_controlled */, PowerPolicyController::ACTION_SUSPEND, - true /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + settings.lid_closed_controlled = true; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); } -// Verifies that idle-related prefs are distilled into the proper WebUI -// settings. +// Verifies that idle-related prefs (when not managed by enterpise policy) +// are distilled into the proper WebUI settings. IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SendIdleSettingForPrefChanges) { - // Set a do-nothing idle action and a nonzero screen-off delay. + // Initial power settings. + DevicePowerSettings settings; + // Set a AC do-nothing idle action and a AC nonzero screen-off delay. User + // should see all three options (DISPLAY_ON, DISPLAY_OFF and + // DISPLAY_OFF_SLEEP) and the selected setting when on AC should be set to + // DISPLAY_OFF. GetPrefs()->Set(ash::prefs::kPowerAcIdleAction, base::Value(PowerPolicyController::ACTION_DO_NOTHING)); GetPrefs()->Set(ash::prefs::kPowerAcScreenOffDelayMs, base::Value(10000)); - EXPECT_EQ(CreateSettingsChangedString(PowerHandler::IdleBehavior::DISPLAY_OFF, - false /* idle_controlled */, - PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, - true /* has_lid */), - GetLastSettingsChangedMessage()); - - // Now set the delay to zero and check that the setting goes to "display on". - GetPrefs()->Set(ash::prefs::kPowerAcScreenOffDelayMs, base::Value(0)); - EXPECT_EQ(CreateSettingsChangedString(PowerHandler::IdleBehavior::DISPLAY_ON, - false /* idle_controlled */, - PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, - true /* has_lid */), - GetLastSettingsChangedMessage()); + + // Current AC idle behavior should be DISPLAY_OFF. + settings.current_ac_behavior = PowerHandler::IdleBehavior::DISPLAY_OFF; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); + + // Now set the battery screen off delay to zero along with battery do-nothing + // idle action and check that the selected setting goes to "display on" when + // on battery. + GetPrefs()->Set(ash::prefs::kPowerBatteryIdleAction, + base::Value(PowerPolicyController::ACTION_DO_NOTHING)); + GetPrefs()->Set(ash::prefs::kPowerBatteryScreenOffDelayMs, base::Value(0)); + + // Current battery idle behavior should be DISPLAY_ON. + settings.current_battery_behavior = PowerHandler::IdleBehavior::DISPLAY_ON; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); // Other idle actions should result in an "other" setting. GetPrefs()->Set(ash::prefs::kPowerAcIdleAction, base::Value(PowerPolicyController::ACTION_STOP_SESSION)); - EXPECT_EQ(CreateSettingsChangedString( - PowerHandler::IdleBehavior::OTHER, false /* idle_controlled */, - PowerPolicyController::ACTION_SUSPEND, - false /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + // Current AC idle behavior should be OTHER. + settings.current_ac_behavior = PowerHandler::IdleBehavior::OTHER; + // Possible AC idle behaviors should include OTHER too. + settings.possible_ac_behaviors.insert(PowerHandler::IdleBehavior::OTHER); + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); +} + +// Verifies that idle-related prefs when managed by enterpise policy are +// distilled into the proper WebUI settings. +IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SendManagedIdleSettingForPrefChanges) { + policy::PolicyMap policy_map; + // Set Enterpise policy that forces AC idle action to suspend. Only possible + // AC idle option visible to the user should be DISPLAY_OFF_SLEEP and the + // current should also be set to same. + SetPolicyForPolicyKey(&policy_map, policy::key::kIdleActionAC, + std::make_unique<base::Value>( + chromeos::PowerPolicyController::ACTION_SUSPEND)); + DevicePowerSettings settings; + std::set<PowerHandler::IdleBehavior> behaviors; + behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP); + settings.possible_ac_behaviors = behaviors; + settings.ac_idle_managed = true; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); + + // Set Enterpise policy that forces battery idle action to Shutdown. Only + // possible battery idle option visible to the user then should be OTHER and + // the default should also be set to same. + SetPolicyForPolicyKey(&policy_map, policy::key::kIdleActionBattery, + std::make_unique<base::Value>( + chromeos::PowerPolicyController::ACTION_SHUT_DOWN)); + behaviors.clear(); + behaviors.insert(PowerHandler::IdleBehavior::OTHER); + settings.possible_battery_behaviors = behaviors; + settings.current_battery_behavior = PowerHandler::IdleBehavior::OTHER; + settings.battery_idle_managed = true; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); + // Erase battery idle action. + policy_map.Erase(policy::key::kIdleActionBattery); + + // Set battery idle action to DO_NOTHING in Enterpise policy. The user then + // should not see DISPLAY_OFF_SLEEP in available options. + SetPolicyForPolicyKey( + &policy_map, policy::key::kIdleActionBattery, + std::make_unique<base::Value>( + chromeos::PowerPolicyController::ACTION_DO_NOTHING)); + behaviors.clear(); + behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_OFF); + behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_ON); + settings.possible_battery_behaviors = behaviors; + settings.current_battery_behavior = PowerHandler::IdleBehavior::DISPLAY_OFF; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); + + // Set battery screen delay in Enterprise policy on top of DO_NOTHING idle + // action. The user should see only see DISPLAY_OFF as the possible battery + // idle action. + SetPolicyForPolicyKey(&policy_map, policy::key::kScreenOffDelayBattery, + std::make_unique<base::Value>(10000)); + behaviors.clear(); + behaviors.insert(PowerHandler::IdleBehavior::DISPLAY_OFF); + settings.possible_battery_behaviors = behaviors; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); + + // Now stop enforcing battery idle action (to DO_NOTHING) in enterprise + // policy. The user should see DISPLAY_OFF and DISPLAY_OFF_SLEEP as + // the possible battery idle actions. + policy_map.Erase(policy::key::kIdleActionBattery); + UpdateChromePolicy(&policy_map); + settings.possible_battery_behaviors.insert( + PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP); + settings.current_battery_behavior = + PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); } // Verifies that the lid-closed pref's value is sent directly to WebUI. IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SendLidSettingForPrefChanges) { GetPrefs()->Set(ash::prefs::kPowerLidClosedAction, base::Value(PowerPolicyController::ACTION_SHUT_DOWN)); - EXPECT_EQ( - CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - false /* idle_controlled */, PowerPolicyController::ACTION_SHUT_DOWN, - false /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + DevicePowerSettings settings; + settings.lid_closed_behavior = PowerPolicyController::ACTION_SHUT_DOWN; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); GetPrefs()->Set(ash::prefs::kPowerLidClosedAction, base::Value(PowerPolicyController::ACTION_STOP_SESSION)); - EXPECT_EQ(CreateSettingsChangedString( - PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, - false /* idle_controlled */, - PowerPolicyController::ACTION_STOP_SESSION, - false /* lid_closed_controlled */, true /* has_lid */), - GetLastSettingsChangedMessage()); + settings.lid_closed_behavior = PowerPolicyController::ACTION_STOP_SESSION; + EXPECT_EQ(ToString(settings), GetLastSettingsChangedMessage()); } // Verifies that requests from WebUI to update the idle behavior update prefs // appropriately. IN_PROC_BROWSER_TEST_F(PowerHandlerTest, SetIdleBehavior) { - // Request the "Keep display on" setting and check that prefs are set + // Request the "Keep display on" AC setting and check that prefs are set // appropriately. - test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_ON); + test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_ON, + true /* is_ac */); EXPECT_EQ(PowerPolicyController::ACTION_DO_NOTHING, GetIntPref(ash::prefs::kPowerAcIdleAction)); EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenDimDelayMs)); EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenOffDelayMs)); EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenLockDelayMs)); - EXPECT_EQ(PowerPolicyController::ACTION_DO_NOTHING, - GetIntPref(ash::prefs::kPowerBatteryIdleAction)); - EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerBatteryScreenDimDelayMs)); - EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerBatteryScreenOffDelayMs)); - EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerBatteryScreenLockDelayMs)); + EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryIdleAction)); + EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenDimDelayMs)); + EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenOffDelayMs)); + EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenLockDelayMs)); - // "Turn off display" should set the idle prefs but clear the screen - // delays. - test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_OFF); + // "Turn off display" battery setting should set the battery idle pref but + // clear the battery screen delays. + test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_OFF, + false /* is_battery */); EXPECT_EQ(PowerPolicyController::ACTION_DO_NOTHING, GetIntPref(ash::prefs::kPowerAcIdleAction)); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcScreenDimDelayMs)); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcScreenOffDelayMs)); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcScreenLockDelayMs)); + EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenDimDelayMs)); + EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenOffDelayMs)); + EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenLockDelayMs)); EXPECT_EQ(PowerPolicyController::ACTION_DO_NOTHING, GetIntPref(ash::prefs::kPowerBatteryIdleAction)); EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenDimDelayMs)); EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenOffDelayMs)); EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenLockDelayMs)); - // Now switch to the "Keep display on" setting (to set the prefs again) and - // check that the "Turn off display and sleep" setting clears all the prefs. - test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_ON); - test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcIdleAction)); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcScreenDimDelayMs)); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcScreenOffDelayMs)); - EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerAcScreenLockDelayMs)); + // Now switch to the "Keep display on" battery setting (to set the prefs + // again) and check that the "Turn off display and sleep" battery setting + // clears all the battery prefs. + test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_ON, + false /* is_battery */); + test_api_->SetIdleBehavior(PowerHandler::IdleBehavior::DISPLAY_OFF_SLEEP, + false /* is_battery */); + EXPECT_EQ(PowerPolicyController::ACTION_DO_NOTHING, + GetIntPref(ash::prefs::kPowerAcIdleAction)); + EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenDimDelayMs)); + EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenOffDelayMs)); + EXPECT_EQ(0, GetIntPref(ash::prefs::kPowerAcScreenLockDelayMs)); EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryIdleAction)); EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenDimDelayMs)); EXPECT_EQ(-1, GetIntPref(ash::prefs::kPowerBatteryScreenOffDelayMs)); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc index 0eebb3c1814..128d85cb80b 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc @@ -6,46 +6,16 @@ #include <algorithm> #include <limits> -#include <numeric> +#include <memory> #include <string> +#include <utility> -#include "base/bind.h" -#include "base/feature_list.h" -#include "base/files/file_util.h" -#include "base/system/sys_info.h" -#include "base/task/post_task.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h" -#include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h" -#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h" -#include "chrome/browser/browsing_data/browsing_data_database_helper.h" -#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h" -#include "chrome/browser/browsing_data/browsing_data_flash_lso_helper.h" -#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h" -#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h" -#include "chrome/browser/browsing_data/browsing_data_service_worker_helper.h" #include "chrome/browser/chromeos/arc/arc_util.h" -#include "chrome/browser/chromeos/crostini/crostini_features.h" -#include "chrome/browser/chromeos/crostini/crostini_manager.h" -#include "chrome/browser/chromeos/crostini/crostini_util.h" -#include "chrome/browser/chromeos/drive/file_system_util.h" #include "chrome/browser/chromeos/file_manager/path_util.h" #include "chrome/browser/platform_util.h" -#include "chrome/browser/profiles/profile.h" #include "chrome/grit/generated_resources.h" -#include "chromeos/cryptohome/cryptohome_util.h" -#include "chromeos/dbus/cryptohome/cryptohome_client.h" -#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/disks/disk.h" #include "components/arc/arc_features.h" -#include "components/arc/arc_prefs.h" -#include "components/arc/arc_service_manager.h" -#include "components/arc/arc_util.h" -#include "components/arc/session/arc_bridge_service.h" -#include "components/browsing_data/content/conditional_cache_counting_helper.h" -#include "components/user_manager/user_manager.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/storage_partition.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/text/bytes_formatting.h" @@ -55,41 +25,43 @@ using chromeos::disks::DiskMountManager; namespace chromeos { namespace settings { -namespace { - -void GetSizeStatBlocking(const base::FilePath& mount_path, - int64_t* total_size, - int64_t* available_size) { - int64_t size = base::SysInfo::AmountOfTotalDiskSpace(mount_path); - if (size >= 0) - *total_size = size; - size = base::SysInfo::AmountOfFreeDiskSpace(mount_path); - if (size >= 0) - *available_size = size; -} - -// Threshold to show a message indicating space is critically low (512 MB). -const int64_t kSpaceCriticallyLowBytes = 512 * 1024 * 1024; -// Threshold to show a message indicating space is low (1 GB). -const int64_t kSpaceLowBytes = 1 * 1024 * 1024 * 1024; +namespace { constexpr char kAndroidEnabled[] = "androidEnabled"; +const char* CalculationTypeToEventName( + calculator::SizeCalculator::CalculationType x) { + switch (x) { + case calculator::SizeCalculator::CalculationType::kSystem: + return "storage-system-size-changed"; + case calculator::SizeCalculator::CalculationType::kInUse: + return "storage-size-stat-changed"; + case calculator::SizeCalculator::CalculationType::kMyFiles: + return "storage-my-files-size-changed"; + case calculator::SizeCalculator::CalculationType::kBrowsingData: + return "storage-browsing-data-size-changed"; + case calculator::SizeCalculator::CalculationType::kAppsExtensions: + return "storage-apps-size-changed"; + case calculator::SizeCalculator::CalculationType::kCrostini: + return "storage-crostini-size-changed"; + case calculator::SizeCalculator::CalculationType::kOtherUsers: + return "storage-other-users-size-changed"; + } + NOTREACHED(); + return ""; +} + } // namespace StorageHandler::StorageHandler(Profile* profile, content::WebUIDataSource* html_source) - : browser_cache_size_(-1), - has_browser_cache_size_(false), - browser_site_data_size_(-1), - has_browser_site_data_size_(false), - updating_downloads_size_(false), - updating_browsing_data_size_(false), - updating_android_size_(false), - updating_crostini_size_(false), - updating_other_users_size_(false), - is_android_running_(false), + : size_stat_calculator_(profile), + my_files_size_calculator_(profile), + browsing_data_size_calculator_(profile), + apps_size_calculator_(profile), + crostini_size_calculator_(profile), + other_users_size_calculator_(), profile_(profile), source_name_(html_source->GetSource()), arc_observer_(this), @@ -101,11 +73,7 @@ StorageHandler::StorageHandler(Profile* profile, } StorageHandler::~StorageHandler() { - DiskMountManager::GetInstance()->RemoveObserver(this); - arc::ArcServiceManager::Get() - ->arc_bridge_service() - ->storage_manager() - ->RemoveObserver(this); + StopObservingEvents(); } void StorageHandler::RegisterMessages() { @@ -120,8 +88,8 @@ void StorageHandler::RegisterMessages() { base::BindRepeating(&StorageHandler::HandleUpdateStorageInfo, base::Unretained(this))); web_ui()->RegisterMessageCallback( - "openDownloads", base::BindRepeating(&StorageHandler::HandleOpenDownloads, - base::Unretained(this))); + "openMyFiles", base::BindRepeating(&StorageHandler::HandleOpenMyFiles, + base::Unretained(this))); web_ui()->RegisterMessageCallback( "openArcStorage", base::BindRepeating(&StorageHandler::HandleOpenArcStorage, @@ -136,35 +104,48 @@ void StorageHandler::OnJavascriptAllowed() { if (base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature)) arc_observer_.Add(arc::ArcSessionManager::Get()); - // Start observing the mojo connection UpdateAndroidSize() relies on. Note - // that OnConnectionReady() will be called immediately if the connection has - // already been established. - arc::ArcServiceManager::Get() - ->arc_bridge_service() - ->storage_manager() - ->AddObserver(this); - // Start observing mount/unmount events to update the connected device list. DiskMountManager::GetInstance()->AddObserver(this); + + // Start observing calculators. + size_stat_calculator_.AddObserver(this); + my_files_size_calculator_.AddObserver(this); + browsing_data_size_calculator_.AddObserver(this); + apps_size_calculator_.AddObserver(this); + crostini_size_calculator_.AddObserver(this); + other_users_size_calculator_.AddObserver(this); } void StorageHandler::OnJavascriptDisallowed() { // Ensure that pending callbacks do not complete and cause JS to be evaluated. weak_ptr_factory_.InvalidateWeakPtrs(); - // Stop observing mount/unmount events to update the connected device list. - DiskMountManager::GetInstance()->RemoveObserver(this); - - // Stop observing the mojo connection so that OnConnectionReady() and - // OnConnectionClosed() that use FireWebUIListener() won't be called while JS - // is disabled. - arc::ArcServiceManager::Get() - ->arc_bridge_service() - ->storage_manager() - ->RemoveObserver(this); - if (base::FeatureList::IsEnabled(arc::kUsbStorageUIFeature)) arc_observer_.Remove(arc::ArcSessionManager::Get()); + + StopObservingEvents(); +} + +int64_t StorageHandler::RoundByteSize(int64_t bytes) { + if (bytes < 0) { + NOTREACHED() << "Negative bytes value"; + return -1; + } + + // Subtract one to the original number of bytes. + bytes--; + // Set all the lower bits to 1. + bytes |= bytes >> 1; + bytes |= bytes >> 2; + bytes |= bytes >> 4; + bytes |= bytes >> 8; + bytes |= bytes >> 16; + bytes |= bytes >> 32; + // Add one. The one bit beyond the highest set bit is set to 1. All the lower + // bits are set to 0. + bytes++; + + return bytes; } void StorageHandler::HandleUpdateAndroidEnabled( @@ -176,20 +157,18 @@ void StorageHandler::HandleUpdateAndroidEnabled( void StorageHandler::HandleUpdateStorageInfo(const base::ListValue* args) { AllowJavascript(); - UpdateSizeStat(); - UpdateDownloadsSize(); - UpdateBrowsingDataSize(); - UpdateAndroidRunning(); - UpdateAndroidSize(); - UpdateCrostiniSize(); - UpdateOtherUsersSize(); + size_stat_calculator_.StartCalculation(); + my_files_size_calculator_.StartCalculation(); + browsing_data_size_calculator_.StartCalculation(); + apps_size_calculator_.StartCalculation(); + crostini_size_calculator_.StartCalculation(); + other_users_size_calculator_.StartCalculation(); } -void StorageHandler::HandleOpenDownloads( - const base::ListValue* unused_args) { - const base::FilePath downloads_path = - file_manager::util::GetDownloadsFolderForProfile(profile_); - platform_util::OpenItem(profile_, downloads_path, platform_util::OPEN_FOLDER, +void StorageHandler::HandleOpenMyFiles(const base::ListValue* unused_args) { + const base::FilePath my_files_path = + file_manager::util::GetMyFilesFolderForProfile(profile_); + platform_util::OpenItem(profile_, my_files_path, platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback()); } @@ -206,233 +185,6 @@ void StorageHandler::HandleUpdateExternalStorages( UpdateExternalStorages(); } -void StorageHandler::UpdateSizeStat() { - const base::FilePath downloads_path = - file_manager::util::GetDownloadsFolderForProfile(profile_); - - int64_t* total_size = new int64_t(0); - int64_t* available_size = new int64_t(0); - base::PostTaskAndReply( - FROM_HERE, - {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE}, - base::Bind(&GetSizeStatBlocking, downloads_path, total_size, - available_size), - base::Bind(&StorageHandler::OnGetSizeStat, weak_ptr_factory_.GetWeakPtr(), - base::Owned(total_size), base::Owned(available_size))); -} - -void StorageHandler::OnGetSizeStat(int64_t* total_size, - int64_t* available_size) { - int64_t used_size = *total_size - *available_size; - base::DictionaryValue size_stat; - size_stat.SetString("totalSize", ui::FormatBytes(*total_size)); - size_stat.SetString("availableSize", ui::FormatBytes(*available_size)); - size_stat.SetString("usedSize", ui::FormatBytes(used_size)); - size_stat.SetDouble("usedRatio", - static_cast<double>(used_size) / *total_size); - int storage_space_state = STORAGE_SPACE_NORMAL; - if (*available_size < kSpaceCriticallyLowBytes) - storage_space_state = STORAGE_SPACE_CRITICALLY_LOW; - else if (*available_size < kSpaceLowBytes) - storage_space_state = STORAGE_SPACE_LOW; - size_stat.SetInteger("spaceState", storage_space_state); - - FireWebUIListener("storage-size-stat-changed", size_stat); -} - -void StorageHandler::UpdateDownloadsSize() { - if (updating_downloads_size_) - return; - updating_downloads_size_ = true; - - const base::FilePath downloads_path = - file_manager::util::GetDownloadsFolderForProfile(profile_); - - base::PostTaskAndReplyWithResult( - FROM_HERE, - {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT}, - base::Bind(&base::ComputeDirectorySize, downloads_path), - base::Bind(&StorageHandler::OnGetDownloadsSize, - weak_ptr_factory_.GetWeakPtr())); -} - -void StorageHandler::OnGetDownloadsSize(int64_t size) { - updating_downloads_size_ = false; - FireWebUIListener("storage-downloads-size-changed", - base::Value(ui::FormatBytes(size))); -} - -void StorageHandler::UpdateBrowsingDataSize() { - if (updating_browsing_data_size_) - return; - updating_browsing_data_size_ = true; - - has_browser_cache_size_ = false; - has_browser_site_data_size_ = false; - // Fetch the size of http cache in browsing data. - browsing_data::ConditionalCacheCountingHelper::Count( - content::BrowserContext::GetDefaultStoragePartition(profile_), - base::Time(), base::Time::Max(), - base::BindOnce(&StorageHandler::OnGetCacheSize, - weak_ptr_factory_.GetWeakPtr())); - - // Fetch the size of site data in browsing data. - if (!site_data_size_collector_.get()) { - content::StoragePartition* storage_partition = - content::BrowserContext::GetDefaultStoragePartition(profile_); - site_data_size_collector_ = std::make_unique<SiteDataSizeCollector>( - storage_partition->GetPath(), - new BrowsingDataCookieHelper(storage_partition), - new BrowsingDataDatabaseHelper(profile_), - new BrowsingDataLocalStorageHelper(profile_), - new BrowsingDataAppCacheHelper(storage_partition->GetAppCacheService()), - new BrowsingDataIndexedDBHelper( - storage_partition->GetIndexedDBContext()), - BrowsingDataFileSystemHelper::Create( - storage_partition->GetFileSystemContext()), - new BrowsingDataServiceWorkerHelper( - storage_partition->GetServiceWorkerContext()), - new BrowsingDataCacheStorageHelper( - storage_partition->GetCacheStorageContext()), - BrowsingDataFlashLSOHelper::Create(profile_)); - } - site_data_size_collector_->Fetch( - base::Bind(&StorageHandler::OnGetBrowsingDataSize, - weak_ptr_factory_.GetWeakPtr(), true)); -} - -void StorageHandler::OnGetCacheSize(bool is_upper_limit, int64_t size) { - DCHECK(!is_upper_limit); - OnGetBrowsingDataSize(false, size); -} - -void StorageHandler::OnGetBrowsingDataSize(bool is_site_data, int64_t size) { - if (is_site_data) { - has_browser_site_data_size_ = true; - browser_site_data_size_ = size; - } else { - has_browser_cache_size_ = true; - browser_cache_size_ = size; - } - if (has_browser_cache_size_ && has_browser_site_data_size_) { - base::string16 size_string; - if (browser_cache_size_ >= 0 && browser_site_data_size_ >= 0) { - size_string = ui::FormatBytes( - browser_site_data_size_ + browser_cache_size_); - } else { - size_string = - l10n_util::GetStringUTF16(IDS_SETTINGS_STORAGE_SIZE_UNKNOWN); - } - updating_browsing_data_size_ = false; - FireWebUIListener("storage-browsing-data-size-changed", - base::Value(size_string)); - } -} - -void StorageHandler::UpdateAndroidRunning() { - FireWebUIListener("storage-android-running-changed", - base::Value(is_android_running_)); -} - -void StorageHandler::UpdateAndroidSize() { - if (!is_android_running_) - return; - - if (updating_android_size_) - return; - updating_android_size_ = true; - - bool success = false; - auto* arc_storage_manager = - arc::ArcStorageManager::GetForBrowserContext(profile_); - if (arc_storage_manager) { - success = arc_storage_manager->GetApplicationsSize(base::BindOnce( - &StorageHandler::OnGetAndroidSize, weak_ptr_factory_.GetWeakPtr())); - } - if (!success) - updating_android_size_ = false; -} - -void StorageHandler::OnGetAndroidSize(bool succeeded, - arc::mojom::ApplicationsSizePtr size) { - base::string16 size_string; - if (succeeded) { - uint64_t total_bytes = size->total_code_bytes + size->total_data_bytes + - size->total_cache_bytes; - size_string = ui::FormatBytes(total_bytes); - } else { - size_string = l10n_util::GetStringUTF16(IDS_SETTINGS_STORAGE_SIZE_UNKNOWN); - } - updating_android_size_ = false; - FireWebUIListener("storage-android-size-changed", base::Value(size_string)); -} - -void StorageHandler::UpdateCrostiniSize() { - if (!crostini::CrostiniFeatures::Get()->IsEnabled(profile_)) { - return; - } - - if (updating_crostini_size_) - return; - updating_crostini_size_ = true; - - crostini::CrostiniManager::GetForProfile(profile_)->ListVmDisks( - base::BindOnce(&StorageHandler::OnGetCrostiniSize, - weak_ptr_factory_.GetWeakPtr())); -} - -void StorageHandler::OnGetCrostiniSize(crostini::CrostiniResult result, - int64_t size) { - updating_crostini_size_ = false; - FireWebUIListener("storage-crostini-size-changed", - base::Value(ui::FormatBytes(size))); -} - -void StorageHandler::UpdateOtherUsersSize() { - if (updating_other_users_size_) - return; - updating_other_users_size_ = true; - - other_users_.clear(); - user_sizes_.clear(); - const user_manager::UserList& users = - user_manager::UserManager::Get()->GetUsers(); - for (auto* user : users) { - if (user->is_active()) - continue; - other_users_.push_back(user); - CryptohomeClient::Get()->GetAccountDiskUsage( - cryptohome::CreateAccountIdentifierFromAccountId(user->GetAccountId()), - base::BindOnce(&StorageHandler::OnGetOtherUserSize, - weak_ptr_factory_.GetWeakPtr())); - } - // We should show "0 B" if there is no other user. - if (other_users_.empty()) { - updating_other_users_size_ = false; - FireWebUIListener("storage-other-users-size-changed", - base::Value(ui::FormatBytes(0))); - } -} - -void StorageHandler::OnGetOtherUserSize( - base::Optional<cryptohome::BaseReply> reply) { - user_sizes_.push_back(cryptohome::AccountDiskUsageReplyToUsageSize(reply)); - if (user_sizes_.size() == other_users_.size()) { - base::string16 size_string; - // If all the requests succeed, shows the total bytes in the UI. - if (std::count(user_sizes_.begin(), user_sizes_.end(), -1) == 0) { - size_string = ui::FormatBytes( - std::accumulate(user_sizes_.begin(), user_sizes_.end(), 0LL)); - } else { - size_string = - l10n_util::GetStringUTF16(IDS_SETTINGS_STORAGE_SIZE_UNKNOWN); - } - updating_other_users_size_ = false; - FireWebUIListener("storage-other-users-size-changed", - base::Value(size_string)); - } -} - void StorageHandler::UpdateExternalStorages() { base::Value devices(base::Value::Type::LIST); for (const auto& itr : DiskMountManager::GetInstance()->mount_points()) { @@ -463,19 +215,7 @@ void StorageHandler::UpdateExternalStorages() { FireWebUIListener("onExternalStoragesUpdated", devices); } -void StorageHandler::OnConnectionReady() { - is_android_running_ = true; - UpdateAndroidRunning(); - UpdateAndroidSize(); -} - -void StorageHandler::OnConnectionClosed() { - is_android_running_ = false; - UpdateAndroidRunning(); -} - void StorageHandler::OnArcPlayStoreEnabledChanged(bool enabled) { - FireWebUIListener("storage-android-enabled-changed", base::Value(enabled)); auto update = std::make_unique<base::DictionaryValue>(); update->SetKey(kAndroidEnabled, base::Value(enabled)); content::WebUIDataSource::Update(profile_, source_name_, std::move(update)); @@ -494,6 +234,114 @@ void StorageHandler::OnMountEvent( UpdateExternalStorages(); } +void StorageHandler::OnSizeCalculated( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes, + const base::Optional<int64_t>& available_bytes) { + if (available_bytes) { + UpdateSizeStat(calculation_type, total_bytes, available_bytes.value()); + } else { + UpdateStorageItem(calculation_type, total_bytes); + } +} + +void StorageHandler::StopObservingEvents() { + // Stop observing mount/unmount events to update the connected device list. + DiskMountManager::GetInstance()->RemoveObserver(this); + + // Stop observing calculators. + size_stat_calculator_.RemoveObserver(this); + my_files_size_calculator_.RemoveObserver(this); + browsing_data_size_calculator_.RemoveObserver(this); + apps_size_calculator_.RemoveObserver(this); + crostini_size_calculator_.RemoveObserver(this); + other_users_size_calculator_.RemoveObserver(this); +} + +void StorageHandler::UpdateStorageItem( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes) { + // When the system size has been calculated, UpdateSystemSize calls this + // method with the calculation type kSystem. This check prevents an infinite + // loop. + if (calculation_type != calculator::SizeCalculator::CalculationType::kSystem) + UpdateSystemSize(calculation_type, total_bytes); + + base::string16 message; + if (total_bytes < 0) { + message = l10n_util::GetStringUTF16(IDS_SETTINGS_STORAGE_SIZE_UNKNOWN); + } else { + message = ui::FormatBytes(total_bytes); + } + + if (calculation_type == + calculator::SizeCalculator::CalculationType::kOtherUsers) { + bool no_other_users = (total_bytes == 0); + FireWebUIListener(CalculationTypeToEventName(calculation_type), + base::Value(message), base::Value(no_other_users)); + } else { + FireWebUIListener(CalculationTypeToEventName(calculation_type), + base::Value(message)); + } +} + +void StorageHandler::UpdateSizeStat( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes, + int64_t available_bytes) { + int64_t rounded_total_bytes = RoundByteSize(total_bytes); + int64_t in_use_total_bytes_ = rounded_total_bytes - available_bytes; + + UpdateSystemSize(calculation_type, in_use_total_bytes_); + + base::DictionaryValue size_stat; + size_stat.SetString("availableSize", ui::FormatBytes(available_bytes)); + size_stat.SetString("usedSize", ui::FormatBytes(in_use_total_bytes_)); + size_stat.SetDouble("usedRatio", static_cast<double>(in_use_total_bytes_) / + rounded_total_bytes); + int storage_space_state = + static_cast<int>(StorageSpaceState::kStorageSpaceNormal); + if (available_bytes < kSpaceCriticallyLowBytes) + storage_space_state = + static_cast<int>(StorageSpaceState::kStorageSpaceCriticallyLow); + else if (available_bytes < kSpaceLowBytes) + storage_space_state = static_cast<int>(StorageSpaceState::kStorageSpaceLow); + size_stat.SetInteger("spaceState", storage_space_state); + + FireWebUIListener(CalculationTypeToEventName(calculation_type), size_stat); +} + +void StorageHandler::UpdateSystemSize( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes) { + const int item_index = static_cast<int>(calculation_type); + storage_items_total_bytes_[item_index] = total_bytes > 0 ? total_bytes : 0; + calculation_state_.set(item_index); + + // Update system size. We only display the total system size when the size of + // all categories has been updated. If some size calculations are pending, + // return early and wait for all calculations to complete. + if (!calculation_state_.all()) + return; + + int64_t system_bytes = 0; + for (int i = 0; i < calculator::SizeCalculator::kCalculationTypeCount; ++i) { + int64_t total_bytes_for_current_item = storage_items_total_bytes_[i]; + // If the storage is in use, add to the system's total storage. + if (i == + static_cast<int>(calculator::SizeCalculator::CalculationType::kInUse)) { + system_bytes += total_bytes_for_current_item; + continue; + } + // Otherwise, this storage amount counts against the total storage + // amount. + system_bytes -= total_bytes_for_current_item; + } + + OnSizeCalculated(calculator::SizeCalculator::CalculationType::kSystem, + system_bytes); +} + bool StorageHandler::IsEligibleForAndroidStorage(std::string source_path) { // Android's StorageManager volume concept relies on assumption that it is // local filesystem. Hence, special volumes like DriveFS should not be diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h index 4c901c9913a..cdd352797a6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h @@ -5,25 +5,12 @@ #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STORAGE_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_STORAGE_HANDLER_H_ -#include <stdint.h> - -#include <memory> #include <string> -#include <vector> -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/optional.h" -#include "base/scoped_observer.h" -#include "chrome/browser/browsing_data/site_data_size_collector.h" #include "chrome/browser/chromeos/arc/session/arc_session_manager.h" +#include "chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" -#include "chromeos/dbus/cryptohome/rpc.pb.h" #include "chromeos/disks/disk_mount_manager.h" -#include "components/arc/mojom/storage_manager.mojom.h" -#include "components/arc/session/connection_observer.h" -#include "components/arc/storage_manager/arc_storage_manager.h" -#include "components/user_manager/user.h" #include "third_party/re2/src/re2/re2.h" class Profile; @@ -39,20 +26,25 @@ enum class CrostiniResult; namespace chromeos { namespace settings { -class StorageHandler - : public ::settings::SettingsPageUIHandler, - public arc::ConnectionObserver<arc::mojom::StorageManagerInstance>, - public arc::ArcSessionManager::Observer, - public chromeos::disks::DiskMountManager::Observer { - public: - // Enumeration for device state about remaining space. These values must be - // kept in sync with settings.StorageSpaceState in JS code. - enum StorageSpaceState { - STORAGE_SPACE_NORMAL = 0, - STORAGE_SPACE_LOW = 1, - STORAGE_SPACE_CRITICALLY_LOW = 2, - }; +// Enumeration for device state about remaining space. These values must be +// kept in sync with settings.StorageSpaceState in JS code. +enum class StorageSpaceState { + kStorageSpaceNormal = 0, + kStorageSpaceLow = 1, + kStorageSpaceCriticallyLow = 2, +}; + +// Threshold to show a message indicating space is critically low (512 MB). +const int64_t kSpaceCriticallyLowBytes = 512 * 1024 * 1024; +// Threshold to show a message indicating space is low (1 GB). +const int64_t kSpaceLowBytes = 1 * 1024 * 1024 * 1024; + +class StorageHandler : public ::settings::SettingsPageUIHandler, + public arc::ArcSessionManager::Observer, + public chromeos::disks::DiskMountManager::Observer, + public calculator::SizeCalculator::Observer { + public: StorageHandler(Profile* profile, content::WebUIDataSource* html_source); ~StorageHandler() override; @@ -61,10 +53,6 @@ class StorageHandler void OnJavascriptAllowed() override; void OnJavascriptDisallowed() override; - // arc::ConnectionObserver<arc::mojom::StorageManagerInstance>: - void OnConnectionReady() override; - void OnConnectionClosed() override; - // arc::ArcSessionManager::Observer: void OnArcPlayStoreEnabledChanged(bool enabled) override; @@ -74,56 +62,42 @@ class StorageHandler const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) override; + // chromeos::settings::calculator::SizeCalculator::Observer: + void OnSizeCalculated( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes, + const base::Optional<int64_t>& available_bytes = base::nullopt) override; + + // Remove the handler from the list of observers of every observed instances. + void StopObservingEvents(); + + protected: + // Round a given number of bytes up to the next power of 2. + // Ex: 14 => 16, 150 => 256. + int64_t RoundByteSize(int64_t bytes); + private: // Handlers of JS messages. void HandleUpdateAndroidEnabled(const base::ListValue* unused_args); void HandleUpdateStorageInfo(const base::ListValue* unused_args); - void HandleOpenDownloads(const base::ListValue* unused_args); + void HandleOpenMyFiles(const base::ListValue* unused_args); void HandleOpenArcStorage(const base::ListValue* unused_args); void HandleUpdateExternalStorages(const base::ListValue* unused_args); - // Requests updating disk space information. - void UpdateSizeStat(); - - // Callback to update the UI about disk space information. - void OnGetSizeStat(int64_t* total_size, int64_t* available_size); - - // Requests updating the size of Downloads directory. - void UpdateDownloadsSize(); - - // Callback to update the UI about the size of Downloads directory. - void OnGetDownloadsSize(int64_t size); - - // Requests updating the size of browsing data. - void UpdateBrowsingDataSize(); - - // Callback to receive the cache size. - void OnGetCacheSize(bool is_upper_limit, int64_t size); - - // Callback to update the UI about the size of browsing data. - void OnGetBrowsingDataSize(bool is_site_data, int64_t size); - - // Requests updating the flag that hides the Android size UI. - void UpdateAndroidRunning(); - - // Requests updating the space size used by Android apps and cache. - void UpdateAndroidSize(); - - // Callback to update the UI about Android apps and cache. - void OnGetAndroidSize(bool succeeded, arc::mojom::ApplicationsSizePtr size); - - // Requests updating the space size used by Crostini VMs and their apps and - // cache. - void UpdateCrostiniSize(); - - // Callback to update the UI about Crostini VMs and their apps and cache. - void OnGetCrostiniSize(crostini::CrostiniResult result, int64_t size); - - // Requests updating the total size of other users' data. - void UpdateOtherUsersSize(); - - // Callback to save the fetched user sizes and update the UI. - void OnGetOtherUserSize(base::Optional<cryptohome::BaseReply> reply); + // Update storage sizes on the UI. + void UpdateStorageItem( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes); + void UpdateSizeStat( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes, + int64_t available_bytes); + + // Marks the size of |item| as calculated. When all storage items have been + // calculated, then "System" size can be calculated. + void UpdateSystemSize( + const calculator::SizeCalculator::CalculationType& calculation_type, + int64_t total_bytes); // Updates list of external storages. void UpdateExternalStorages(); @@ -132,38 +106,21 @@ class StorageHandler // storage. bool IsEligibleForAndroidStorage(std::string source_path); - // Total size of cache data in browsing data. - int64_t browser_cache_size_; - - // True if we have already received the size of http cache. - bool has_browser_cache_size_; - - // Total size of site data in browsing data. - int64_t browser_site_data_size_; - - // True if we have already received the size of site data. - bool has_browser_site_data_size_; - - // Helper to compute the total size of all types of site date. - std::unique_ptr<SiteDataSizeCollector> site_data_size_collector_; - - // The list of other users whose directory sizes will be accumulated as the - // size of "Other users". - user_manager::UserList other_users_; - - // Fetched sizes of user directories. - std::vector<int64_t> user_sizes_; - - // Flags indicating fetch operations for storage sizes are ongoing. - bool updating_downloads_size_; - bool updating_browsing_data_size_; - bool updating_android_size_; - bool updating_crostini_size_; - bool updating_other_users_size_; - - // A flag for keeping track of the mojo connection status to the ARC - // container. - bool is_android_running_; + // Instances calculating the size of each storage items. + calculator::SizeStatCalculator size_stat_calculator_; + calculator::MyFilesSizeCalculator my_files_size_calculator_; + calculator::BrowsingDataSizeCalculator browsing_data_size_calculator_; + calculator::AppsSizeCalculator apps_size_calculator_; + calculator::CrostiniSizeCalculator crostini_size_calculator_; + calculator::OtherUsersSizeCalculator other_users_size_calculator_; + + // Controls if the size of each storage item has been calculated. + std::bitset<calculator::SizeCalculator::kCalculationTypeCount> + calculation_state_; + + // Keeps track of the size of each storage item. + int64_t storage_items_total_bytes_ + [calculator::SizeCalculator::kCalculationTypeCount] = {0}; Profile* const profile_; const std::string source_name_; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc new file mode 100644 index 00000000000..57c1ab89f3c --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc @@ -0,0 +1,516 @@ +// Copyright 2020 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 <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" +#include "base/system/sys_info.h" +#include "chrome/browser/chromeos/arc/session/arc_session_manager.h" +#include "chrome/browser/chromeos/arc/test/test_arc_session_manager.h" +#include "chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h" +#include "chrome/browser/chromeos/file_manager/path_util.h" +#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h" +#include "chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "components/arc/arc_service_manager.h" +#include "components/arc/test/fake_arc_session.h" +#include "content/public/browser/web_ui_data_source.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_web_ui.h" +#include "storage/browser/file_system/external_mount_points.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/text/bytes_formatting.h" + +namespace chromeos { +namespace settings { + +namespace { + +const char kLsbRelease[] = + "CHROMEOS_RELEASE_NAME=Chrome OS\n" + "CHROMEOS_RELEASE_VERSION=1.2.3.4\n"; + +class TestStorageHandler : public StorageHandler { + public: + explicit TestStorageHandler(Profile* profile, + content::WebUIDataSource* html_source) + : StorageHandler(profile, html_source) {} + + // Pull WebUIMessageHandler::set_web_ui() into public so tests can call it. + using StorageHandler::RoundByteSize; + using StorageHandler::set_web_ui; +}; + +class StorageHandlerTest : public testing::Test { + public: + StorageHandlerTest() = default; + ~StorageHandlerTest() override = default; + + void SetUp() override { + // The storage handler requires an instance of DiskMountManager, + // ArcServiceManager and ArcSessionManager. + chromeos::disks::DiskMountManager::InitializeForTesting( + new file_manager::FakeDiskMountManager); + arc_service_manager_ = std::make_unique<arc::ArcServiceManager>(); + arc_session_manager_ = arc::CreateTestArcSessionManager( + std::make_unique<arc::ArcSessionRunner>( + base::BindRepeating(arc::FakeArcSession::Create))); + + // Initialize profile. + profile_manager_ = std::make_unique<TestingProfileManager>( + TestingBrowserProcess::GetGlobal()); + ASSERT_TRUE(profile_manager_->SetUp()); + profile_ = profile_manager_->CreateTestingProfile("p1"); + + // Initialize storage handler. + content::WebUIDataSource* html_source = + content::WebUIDataSource::Create(chrome::kChromeUIOSSettingsHost); + handler_ = std::make_unique<TestStorageHandler>(profile_, html_source); + handler_->set_web_ui(&web_ui_); + handler_->AllowJavascriptForTesting(); + + // Initialize tests APIs. + size_stat_test_api_ = std::make_unique<calculator::SizeStatTestAPI>( + handler_.get(), new calculator::SizeStatCalculator(profile_)); + my_files_size_test_api_ = std::make_unique<calculator::MyFilesSizeTestAPI>( + handler_.get(), new calculator::MyFilesSizeCalculator(profile_)); + browsing_data_size_test_api_ = + std::make_unique<calculator::BrowsingDataSizeTestAPI>( + handler_.get(), + new calculator::BrowsingDataSizeCalculator(profile_)); + apps_size_test_api_ = std::make_unique<calculator::AppsSizeTestAPI>( + handler_.get(), new calculator::AppsSizeCalculator(profile_)); + crostini_size_test_api_ = std::make_unique<calculator::CrostiniSizeTestAPI>( + handler_.get(), new calculator::CrostiniSizeCalculator(profile_)); + other_users_size_test_api_ = + std::make_unique<calculator::OtherUsersSizeTestAPI>( + handler_.get(), new calculator::OtherUsersSizeCalculator()); + + // Create and register My files directory. + // By emulating chromeos running, GetMyFilesFolderForProfile will return the + // profile's temporary location instead of $HOME/Downloads. + chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, + base::Time()); + const base::FilePath my_files_path = + file_manager::util::GetMyFilesFolderForProfile(profile_); + CHECK(base::CreateDirectory(my_files_path)); + CHECK(storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + file_manager::util::GetDownloadsMountPointName(profile_), + storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(), + my_files_path)); + } + + void TearDown() override { + handler_.reset(); + size_stat_test_api_.reset(); + my_files_size_test_api_.reset(); + browsing_data_size_test_api_.reset(); + apps_size_test_api_.reset(); + crostini_size_test_api_.reset(); + other_users_size_test_api_.reset(); + chromeos::disks::DiskMountManager::Shutdown(); + storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems(); + } + + protected: + // From a given amount of total size and available size as input, returns the + // space state determined by the OnGetSizeState function. + int GetSpaceState(int64_t* total_size, int64_t* available_size) { + size_stat_test_api_->SimulateOnGetSizeStat(total_size, available_size); + task_environment_.RunUntilIdle(); + const base::Value* dictionary = + GetWebUICallbackMessage("storage-size-stat-changed"); + EXPECT_TRUE(dictionary) << "No 'storage-size-stat-changed' callback"; + int space_state = dictionary->FindKey("spaceState")->GetInt(); + return space_state; + } + + // Expects a callback message with a given |event_name|. A non null + // base::Value is returned if the callback message is found and has associated + // data. + const base::Value* GetWebUICallbackMessage(const std::string& event_name) { + for (auto it = web_ui_.call_data().rbegin(); + it != web_ui_.call_data().rend(); ++it) { + const content::TestWebUI::CallData* data = it->get(); + std::string name; + if (data->function_name() != "cr.webUIListenerCallback" || + !data->arg1()->GetAsString(&name)) { + continue; + } + if (name == event_name) + return data->arg2(); + } + return nullptr; + } + + // Get the path to file manager's test data directory. + base::FilePath GetTestDataFilePath(const std::string& file_name) { + // Get the path to file manager's test data directory. + base::FilePath source_dir; + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_dir)); + base::FilePath test_data_dir = source_dir.AppendASCII("chrome") + .AppendASCII("test") + .AppendASCII("data") + .AppendASCII("chromeos") + .AppendASCII("file_manager"); + + // Return full test data path to the given |file_name|. + return test_data_dir.Append(base::FilePath::FromUTF8Unsafe(file_name)); + } + + // Copy a file from the file manager's test data directory to the specified + // target_path. + void AddFile(const std::string& file_name, + int64_t expected_size, + base::FilePath target_path) { + const base::FilePath entry_path = GetTestDataFilePath(file_name); + target_path = target_path.AppendASCII(file_name); + ASSERT_TRUE(base::CopyFile(entry_path, target_path)) + << "Copy from " << entry_path.value() << " to " << target_path.value() + << " failed."; + // Verify file size. + base::stat_wrapper_t stat; + const int res = base::File::Lstat(target_path.value().c_str(), &stat); + ASSERT_FALSE(res < 0) << "Couldn't stat" << target_path.value(); + ASSERT_EQ(expected_size, stat.st_size); + } + + std::unique_ptr<TestStorageHandler> handler_; + content::TestWebUI web_ui_; + content::BrowserTaskEnvironment task_environment_; + std::unique_ptr<TestingProfileManager> profile_manager_; + Profile* profile_; + std::unique_ptr<calculator::SizeStatTestAPI> size_stat_test_api_; + std::unique_ptr<calculator::MyFilesSizeTestAPI> my_files_size_test_api_; + std::unique_ptr<calculator::BrowsingDataSizeTestAPI> + browsing_data_size_test_api_; + std::unique_ptr<calculator::AppsSizeTestAPI> apps_size_test_api_; + std::unique_ptr<calculator::CrostiniSizeTestAPI> crostini_size_test_api_; + std::unique_ptr<calculator::OtherUsersSizeTestAPI> other_users_size_test_api_; + + private: + std::unique_ptr<arc::ArcServiceManager> arc_service_manager_; + std::unique_ptr<arc::ArcSessionManager> arc_session_manager_; + DISALLOW_COPY_AND_ASSIGN(StorageHandlerTest); +}; + +TEST_F(StorageHandlerTest, RoundByteSize) { + static const struct { + int64_t bytes; + const char* expected; + } cases[] = { + {0, "0 B"}, + {3, "4 B"}, + {4, "4 B"}, + {5, "8 B"}, + {8 * 1024 - 1, "8.0 KB"}, + {8 * 1024, "8.0 KB"}, + {8 * 1024 + 1, "16.0 KB"}, + {31 * 1024 * 1024, "32.0 MB"}, + {32 * 1024 * 1024, "32.0 MB"}, + {50 * 1024 * 1024, "64.0 MB"}, + {65LL * 1024 * 1024 * 1024, "128 GB"}, + {130LL * 1024 * 1024 * 1024, "256 GB"}, + {130LL * 1024 * 1024 * 1024, "256 GB"}, + {1LL * 1024 * 1024 * 1024 * 1024, "1.0 TB"}, + {1LL * 1024 * 1024 * 1024 * 1024 + 1, "2.0 TB"}, + {(1LL << 61) + 1, "4,096 PB"}, + }; + + for (auto& c : cases) { + int64_t rounded_bytes = handler_->RoundByteSize(c.bytes); + EXPECT_EQ(base::ASCIIToUTF16(c.expected), ui::FormatBytes(rounded_bytes)); + } +} + +TEST_F(StorageHandlerTest, GlobalSizeStat) { + // Get local filesystem storage statistics. + const base::FilePath mount_path = + file_manager::util::GetMyFilesFolderForProfile(profile_); + int64_t total_size = base::SysInfo::AmountOfTotalDiskSpace(mount_path); + int64_t available_size = base::SysInfo::AmountOfFreeDiskSpace(mount_path); + + // Round the total size. + int64_t rounded_total_size = handler_->RoundByteSize(total_size); + int64_t used_size = rounded_total_size - available_size; + double used_ratio = static_cast<double>(used_size) / rounded_total_size; + + // Get statistics from storage handler's UpdateSizeStat. + size_stat_test_api_->StartCalculation(); + task_environment_.RunUntilIdle(); + + const base::Value* dictionary = + GetWebUICallbackMessage("storage-size-stat-changed"); + ASSERT_TRUE(dictionary) << "No 'storage-size-stat-changed' callback"; + + const std::string& storage_handler_available_size = + dictionary->FindKey("availableSize")->GetString(); + const std::string& storage_handler_used_size = + dictionary->FindKey("usedSize")->GetString(); + double storage_handler_used_ratio = + dictionary->FindKey("usedRatio")->GetDouble(); + + EXPECT_EQ(ui::FormatBytes(available_size), + base::ASCIIToUTF16(storage_handler_available_size)); + EXPECT_EQ(ui::FormatBytes(used_size), + base::ASCIIToUTF16(storage_handler_used_size)); + double diff = used_ratio > storage_handler_used_ratio + ? used_ratio - storage_handler_used_ratio + : storage_handler_used_ratio - used_ratio; + // Running the test while writing data on disk (~400MB/s), the difference + // between the values returned by the two AmountOfFreeDiskSpace calls is never + // more than 100KB. By expecting diff to be less than 100KB / + // rounded_total_size, the test is very unlikely to be flaky. + EXPECT_LE(diff, static_cast<double>(100 * 1024) / rounded_total_size); +} + +TEST_F(StorageHandlerTest, StorageSpaceState) { + // Less than 512 MB available, space state is critically low. + int64_t total_size = 1024 * 1024 * 1024; + int64_t available_size = 512 * 1024 * 1024 - 1; + int space_state = GetSpaceState(&total_size, &available_size); + EXPECT_EQ(static_cast<int>(StorageSpaceState::kStorageSpaceCriticallyLow), + space_state); + + // Less than 1GB available, space state is low. + available_size = 512 * 1024 * 1024; + space_state = GetSpaceState(&total_size, &available_size); + EXPECT_EQ(static_cast<int>(StorageSpaceState::kStorageSpaceLow), space_state); + available_size = 1024 * 1024 * 1024 - 1; + space_state = GetSpaceState(&total_size, &available_size); + EXPECT_EQ(static_cast<int>(StorageSpaceState::kStorageSpaceLow), space_state); + + // From 1GB, normal space state. + available_size = 1024 * 1024 * 1024; + space_state = GetSpaceState(&total_size, &available_size); + EXPECT_EQ(static_cast<int>(StorageSpaceState::kStorageSpaceNormal), + space_state); +} + +TEST_F(StorageHandlerTest, MyFilesSize) { + base::ScopedAllowBlockingForTesting allow_blocking; + + const base::FilePath my_files_path = + file_manager::util::GetMyFilesFolderForProfile(profile_); + const base::FilePath downloads_path = + file_manager::util::GetDownloadsFolderForProfile(profile_); + const base::FilePath android_files_path = + profile_->GetPath().Append("AndroidFiles"); + const base::FilePath android_files_download_path = + android_files_path.Append("Download"); + + // Create directories. + CHECK(base::CreateDirectory(downloads_path)); + CHECK(base::CreateDirectory(android_files_path)); + CHECK(base::CreateDirectory(android_files_download_path)); + + // Register android files mount point. + CHECK(storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( + file_manager::util::GetAndroidFilesMountPointName(), + storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(), + android_files_path)); + + // Add files in My files and android files. + AddFile("random.bin", 8092, my_files_path); // ~7.9 KB + AddFile("tall.pdf", 15271, android_files_path); // ~14.9 KB + // Add file in Downloads and simulate bind mount with + // [android files]/Download. + AddFile("video.ogv", 59943, downloads_path); // ~58.6 KB + AddFile("video.ogv", 59943, android_files_download_path); + + // Calculate My files size. + my_files_size_test_api_->StartCalculation(); + task_environment_.RunUntilIdle(); + + const base::Value* callback = + GetWebUICallbackMessage("storage-my-files-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-my-files-size-changed' callback"; + + // Check return value. + EXPECT_EQ("81.4 KB", callback->GetString()); +} + +TEST_F(StorageHandlerTest, AppsExtensionsSize) { + // The data for apps and extensions apps_size_test_api_installed from the + // webstore is stored in the Extensions folder. Add data at a random location + // in the Extensions folder and check UI callback message. + const base::FilePath extensions_data_path = + profile_->GetPath() + .AppendASCII("Extensions") + .AppendASCII("fake_extension_id"); + CHECK(base::CreateDirectory(extensions_data_path)); + AddFile("id3Audio.mp3", 180999, extensions_data_path); // ~177 KB + + // Calculate web store apps and extensions size. + apps_size_test_api_->StartCalculation(); + task_environment_.RunUntilIdle(); + + const base::Value* callback = + GetWebUICallbackMessage("storage-apps-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-apps-size-changed' callback"; + + // Check return value. + EXPECT_EQ("177 KB", callback->GetString()); + + // Simulate android apps size callback. + // 592840 + 25284 + 9987 = 628111 ~613 KB. + apps_size_test_api_->SimulateOnGetAndroidAppsSize(true /* succeeded */, + 592840, 25284, 9987); + task_environment_.RunUntilIdle(); + + callback = GetWebUICallbackMessage("storage-apps-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-apps-size-changed' callback"; + + // Check return value. + EXPECT_EQ("790 KB", callback->GetString()); + + // Add more data in the Extensions folder. Android is not running and the size + // of android apps is back down to 0 B. + AddFile("video_long.ogv", 230096, extensions_data_path); // ~225 KB + + // Calculate web store apps and extensions size. + apps_size_test_api_->StartCalculation(); + task_environment_.RunUntilIdle(); + + callback = GetWebUICallbackMessage("storage-apps-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-apps-size-changed' callback"; + + // Check return value. + EXPECT_EQ("401 KB", callback->GetString()); +} + +TEST_F(StorageHandlerTest, SystemSize) { + // The "System" row on the storage page displays the difference between the + // total amount of used space and the sum of the sizes of the different + // storage items of the storage page (My files, Browsing data, apps etc...) + // This test simulates callbacks from each one of these storage items; the + // calculation of the "System" size should only happen when all of the other + // storage items have been calculated. + const int64_t KB = 1024; + const int64_t MB = 1024 * KB; + const int64_t GB = 1024 * MB; + const int64_t TB = 1024 * GB; + + // Simulate size stat callback. + int64_t total_size = TB; + int64_t available_size = 100 * GB; + size_stat_test_api_->SimulateOnGetSizeStat(&total_size, &available_size); + const base::Value* callback = + GetWebUICallbackMessage("storage-size-stat-changed"); + ASSERT_TRUE(callback) << "No 'storage-size-stat-changed' callback"; + EXPECT_EQ("100 GB", callback->FindKey("availableSize")->GetString()); + EXPECT_EQ("924 GB", callback->FindKey("usedSize")->GetString()); + // Expect no system size callback until every other item has been updated. + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + + // Simulate my files size callback. + my_files_size_test_api_->SimulateOnGetTotalBytes(400 * GB); + callback = GetWebUICallbackMessage("storage-my-files-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-my-files-size-changed' callback"; + EXPECT_EQ("400 GB", callback->GetString()); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + + // Simulate browsing data callbacks. Has to be called with + // both |is_data_site| = true and false. + browsing_data_size_test_api_->SimulateOnGetBrowsingDataSize( + true /* is_site_data */, 10 * GB); + ASSERT_FALSE(GetWebUICallbackMessage("storage-browsing-data-size-changed")); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + browsing_data_size_test_api_->SimulateOnGetBrowsingDataSize( + false /* is_site_data */, 14 * GB); + callback = GetWebUICallbackMessage("storage-browsing-data-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-browsing-data-size-changed' callback"; + EXPECT_EQ("24.0 GB", callback->GetString()); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + + // Simulate apps and extensions size callbacks. + apps_size_test_api_->SimulateOnGetAppsSize(29 * GB); + apps_size_test_api_->SimulateOnGetAndroidAppsSize(false, 0, 0, 0); + callback = GetWebUICallbackMessage("storage-apps-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-apps-size-changed' callback"; + EXPECT_EQ("29.0 GB", callback->GetString()); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + apps_size_test_api_->SimulateOnGetAndroidAppsSize( + true /* succeeded */, 724 * MB, 100 * MB, 200 * MB); + callback = GetWebUICallbackMessage("storage-apps-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-apps-size-changed' callback"; + EXPECT_EQ("30.0 GB", callback->GetString()); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + + // Simulate crostini size callback. + crostini_size_test_api_->SimulateOnGetCrostiniSize(70 * GB); + callback = GetWebUICallbackMessage("storage-crostini-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-crostini-size-changed' callback"; + EXPECT_EQ("70.0 GB", callback->GetString()); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + + // Simulate other users size callback. No callback message until the sizes of + // every users is calculated. + std::vector<int64_t> other_user_sizes = + std::vector<int64_t>{200 * GB, 50 * GB, 50 * GB}; + other_users_size_test_api_->InitializeOtherUserSize(other_user_sizes.size()); + for (std::size_t i = 0; i < other_user_sizes.size(); i++) { + cryptohome::BaseReply result; + result.set_error(cryptohome::CRYPTOHOME_ERROR_NOT_SET); + cryptohome::GetAccountDiskUsageReply* usage_reply = + result.MutableExtension(cryptohome::GetAccountDiskUsageReply::reply); + usage_reply->set_size(other_user_sizes[i]); + base::Optional<cryptohome::BaseReply> reply = std::move(result); + other_users_size_test_api_->SimulateOnGetOtherUserSize(reply); + if (i < other_user_sizes.size() - 1) { + ASSERT_FALSE(GetWebUICallbackMessage("storage-other-users-size-changed")); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + } else { + // When the size of the last user's cryptohome is calculated, we expect a + // callback with the "Other users" size. + callback = GetWebUICallbackMessage("storage-other-users-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-other-users-size-changed' callback"; + EXPECT_EQ("300 GB", callback->GetString()); + // Every item size has been calculated, system size should also be + // updated. + callback = GetWebUICallbackMessage("storage-system-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-system-size-changed' callback"; + EXPECT_EQ("100 GB", callback->GetString()); + } + } + + // If there's an error while calculating the size of browsing data, the size + // of browsing data and system should be displayed as "Unknown". + browsing_data_size_test_api_->SimulateOnGetBrowsingDataSize( + true /* is_site_data */, -1); + callback = GetWebUICallbackMessage("storage-browsing-data-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-browsing-data-size-changed' callback"; + EXPECT_EQ("Unknown", callback->GetString()); + // The missing 24.0 GB of browsing data should be reflected in the system + // section instead. We expect the displayed size to be 100 + 24 GB. + callback = GetWebUICallbackMessage("storage-system-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-system-size-changed' callback"; + EXPECT_EQ("124 GB", callback->GetString()); + + // No error while recalculating browsing data size, the UI should be updated + // with the right sizes. + browsing_data_size_test_api_->SimulateOnGetBrowsingDataSize( + true /* is_site_data */, 10 * GB); + callback = GetWebUICallbackMessage("storage-browsing-data-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-browsing-data-size-changed' callback"; + EXPECT_EQ("24.0 GB", callback->GetString()); + callback = GetWebUICallbackMessage("storage-system-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-system-size-changed' callback"; + EXPECT_EQ("100 GB", callback->GetString()); +} + +} // namespace + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc index 601eeaf2435..0a90a0e3039 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc @@ -20,9 +20,7 @@ #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/prefs/pref_service.h" -#include "content/public/browser/system_connector.h" -#include "services/device/public/mojom/constants.mojom.h" -#include "services/service_manager/public/cpp/connector.h" +#include "content/public/browser/device_service.h" #include "ui/base/l10n/l10n_util.h" using session_manager::SessionManager; @@ -57,8 +55,8 @@ std::unique_ptr<base::DictionaryValue> GetFingerprintsInfo( } // namespace FingerprintHandler::FingerprintHandler(Profile* profile) : profile_(profile) { - content::GetSystemConnector()->Connect( - device::mojom::kServiceName, fp_service_.BindNewPipeAndPassReceiver()); + content::GetDeviceService().BindFingerprint( + fp_service_.BindNewPipeAndPassReceiver()); user_id_ = ProfileHelper::Get()->GetUserIdHashFromProfile(profile); } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc index e75796ecd32..bd301cfcb91 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_handler.cc @@ -143,7 +143,8 @@ void InternetHandler::ConfigureThirdPartyVpn(const base::ListValue* args) { return; } if (network->type() != shill::kTypeVPN) { - NET_LOG(ERROR) << "ConfigureThirdPartyVpn: Network is not a VPN: " << guid; + NET_LOG(ERROR) << "ConfigureThirdPartyVpn: Network is not a VPN: " + << NetworkId(network); return; } @@ -169,7 +170,8 @@ void InternetHandler::ConfigureThirdPartyVpn(const base::ListValue* args) { } NET_LOG(ERROR) << "ConfigureThirdPartyVpn: Unsupported VPN type: " - << network->GetVpnProviderType() << " For: " << guid; + << network->GetVpnProviderType() + << " For: " << NetworkId(network); } void InternetHandler::RequestGmsCoreNotificationsDisabledDeviceNames( diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.cc new file mode 100644 index 00000000000..90a75cf7418 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.cc @@ -0,0 +1,252 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/internet_strings_provider.h" + +#include "ash/public/cpp/network_config_service.h" +#include "base/bind.h" +#include "base/no_destructor.h" +#include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h" +#include "chrome/browser/ui/webui/webui_util.h" +#include "chrome/common/url_constants.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/grit/chromium_strings.h" +#include "chrome/grit/generated_resources.h" +#include "chromeos/strings/grit/chromeos_strings.h" +#include "components/strings/grit/components_strings.h" +#include "content/public/browser/web_ui_data_source.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/webui/web_ui_util.h" + +namespace chromeos { +namespace settings { +namespace { + +const std::vector<SearchConcept>& GetNetworkSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_SETTINGS_TAG_NETWORK_SETTINGS, + chrome::kNetworksSubPage, + mojom::SearchResultIcon::kWifi, + {IDS_SETTINGS_TAG_NETWORK_SETTINGS_ALT1, SearchConcept::kAltTagEnd}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetEthernetSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_SETTINGS_TAG_ETHERNET_SETTINGS, + chrome::kEthernetSettingsSubPage, + mojom::SearchResultIcon::kEthernet, + {IDS_SETTINGS_TAG_ETHERNET_SETTINGS_ALT1, SearchConcept::kAltTagEnd}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetWifiSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_SETTINGS_TAG_WIFI_SETTINGS, chrome::kWiFiSettingsSubPage, + mojom::SearchResultIcon::kWifi}, + {IDS_SETTINGS_TAG_TURN_ON_WIFI, + chrome::kWiFiSettingsSubPage, + mojom::SearchResultIcon::kWifi, + {IDS_SETTINGS_TAG_TURN_ON_WIFI_ALT1, SearchConcept::kAltTagEnd}}, + {IDS_SETTINGS_TAG_TURN_OFF_WIFI, + chrome::kWiFiSettingsSubPage, + mojom::SearchResultIcon::kWifi, + {IDS_SETTINGS_TAG_TURN_OFF_WIFI_ALT1, SearchConcept::kAltTagEnd}}, + {IDS_SETTINGS_TAG_CONNECT_WIFI, chrome::kWiFiSettingsSubPage, + mojom::SearchResultIcon::kWifi}, + {IDS_SETTINGS_TAG_DISCONNECT_WIFI, chrome::kWiFiSettingsSubPage, + mojom::SearchResultIcon::kWifi}, + }); + return *tags; +} + +} // namespace + +InternetStringsProvider::InternetStringsProvider(Profile* profile, + Delegate* per_page_delegate) + : OsSettingsPerPageStringsProvider(profile, per_page_delegate) { + // General network search tags are always added. + delegate()->AddSearchTags(GetNetworkSearchConcepts()); + + // Receive updates when devices (e.g., Ethernet, Wi-Fi) go on/offline. + ash::GetNetworkConfigService( + cros_network_config_.BindNewPipeAndPassReceiver()); + cros_network_config_->AddObserver(receiver_.BindNewPipeAndPassRemote()); + + // Fetch initial list of devices. + FetchDeviceList(); +} + +InternetStringsProvider::~InternetStringsProvider() = default; + +void InternetStringsProvider::AddUiStrings( + content::WebUIDataSource* html_source) const { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"internetAddConnection", IDS_SETTINGS_INTERNET_ADD_CONNECTION}, + {"internetAddConnectionExpandA11yLabel", + IDS_SETTINGS_INTERNET_ADD_CONNECTION_EXPAND_ACCESSIBILITY_LABEL}, + {"internetAddConnectionNotAllowed", + IDS_SETTINGS_INTERNET_ADD_CONNECTION_NOT_ALLOWED}, + {"internetAddThirdPartyVPN", IDS_SETTINGS_INTERNET_ADD_THIRD_PARTY_VPN}, + {"internetAddVPN", IDS_SETTINGS_INTERNET_ADD_VPN}, + {"internetAddWiFi", IDS_SETTINGS_INTERNET_ADD_WIFI}, + {"internetConfigName", IDS_SETTINGS_INTERNET_CONFIG_NAME}, + {"internetDetailPageTitle", IDS_SETTINGS_INTERNET_DETAIL}, + {"internetDeviceEnabling", IDS_SETTINGS_INTERNET_DEVICE_ENABLING}, + {"internetDeviceInitializing", IDS_SETTINGS_INTERNET_DEVICE_INITIALIZING}, + {"internetJoinType", IDS_SETTINGS_INTERNET_JOIN_TYPE}, + {"internetKnownNetworksPageTitle", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS}, + {"internetMobileSearching", IDS_SETTINGS_INTERNET_MOBILE_SEARCH}, + {"internetNoNetworks", IDS_SETTINGS_INTERNET_NO_NETWORKS}, + {"internetPageTitle", IDS_SETTINGS_INTERNET}, + {"internetSummaryButtonA11yLabel", + IDS_SETTINGS_INTERNET_SUMMARY_BUTTON_ACCESSIBILITY_LABEL}, + {"internetToggleMobileA11yLabel", + IDS_SETTINGS_INTERNET_TOGGLE_MOBILE_ACCESSIBILITY_LABEL}, + {"internetToggleTetherLabel", IDS_SETTINGS_INTERNET_TOGGLE_TETHER_LABEL}, + {"internetToggleTetherSubtext", + IDS_SETTINGS_INTERNET_TOGGLE_TETHER_SUBTEXT}, + {"internetToggleWiFiA11yLabel", + IDS_SETTINGS_INTERNET_TOGGLE_WIFI_ACCESSIBILITY_LABEL}, + {"knownNetworksAll", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_ALL}, + {"knownNetworksButton", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_BUTTON}, + {"knownNetworksMessage", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MESSAGE}, + {"knownNetworksPreferred", + IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_PREFFERED}, + {"knownNetworksMenuAddPreferred", + IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_ADD_PREFERRED}, + {"knownNetworksMenuRemovePreferred", + IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_REMOVE_PREFERRED}, + {"knownNetworksMenuForget", + IDS_SETTINGS_INTERNET_KNOWN_NETWORKS_MENU_FORGET}, + {"networkAllowDataRoaming", + IDS_SETTINGS_SETTINGS_NETWORK_ALLOW_DATA_ROAMING}, + {"networkAllowDataRoamingEnabledHome", + IDS_SETTINGS_SETTINGS_NETWORK_ALLOW_DATA_ROAMING_ENABLED_HOME}, + {"networkAllowDataRoamingEnabledRoaming", + IDS_SETTINGS_SETTINGS_NETWORK_ALLOW_DATA_ROAMING_ENABLED_ROAMING}, + {"networkAllowDataRoamingDisabled", + IDS_SETTINGS_SETTINGS_NETWORK_ALLOW_DATA_ROAMING_DISABLED}, + {"networkAlwaysOnVpn", IDS_SETTINGS_INTERNET_NETWORK_ALWAYS_ON_VPN}, + {"networkAutoConnect", IDS_SETTINGS_INTERNET_NETWORK_AUTO_CONNECT}, + {"networkAutoConnectCellular", + IDS_SETTINGS_INTERNET_NETWORK_AUTO_CONNECT_CELLULAR}, + {"networkButtonActivate", IDS_SETTINGS_INTERNET_BUTTON_ACTIVATE}, + {"networkButtonConfigure", IDS_SETTINGS_INTERNET_BUTTON_CONFIGURE}, + {"networkButtonConnect", IDS_SETTINGS_INTERNET_BUTTON_CONNECT}, + {"networkButtonDisconnect", IDS_SETTINGS_INTERNET_BUTTON_DISCONNECT}, + {"networkButtonForget", IDS_SETTINGS_INTERNET_BUTTON_FORGET}, + {"networkButtonViewAccount", IDS_SETTINGS_INTERNET_BUTTON_VIEW_ACCOUNT}, + {"networkConnectNotAllowed", IDS_SETTINGS_INTERNET_CONNECT_NOT_ALLOWED}, + {"networkIPAddress", IDS_SETTINGS_INTERNET_NETWORK_IP_ADDRESS}, + {"networkIPConfigAuto", IDS_SETTINGS_INTERNET_NETWORK_IP_CONFIG_AUTO}, + {"networkNameserversLearnMore", IDS_LEARN_MORE}, + {"networkPrefer", IDS_SETTINGS_INTERNET_NETWORK_PREFER}, + {"networkPrimaryUserControlled", + IDS_SETTINGS_INTERNET_NETWORK_PRIMARY_USER_CONTROLLED}, + {"networkScanningLabel", IDS_NETWORK_SCANNING_MESSAGE}, + {"networkSectionAdvanced", + IDS_SETTINGS_INTERNET_NETWORK_SECTION_ADVANCED}, + {"networkSectionAdvancedA11yLabel", + IDS_SETTINGS_INTERNET_NETWORK_SECTION_ADVANCED_ACCESSIBILITY_LABEL}, + {"networkSectionNetwork", IDS_SETTINGS_INTERNET_NETWORK_SECTION_NETWORK}, + {"networkSectionNetworkExpandA11yLabel", + IDS_SETTINGS_INTERNET_NETWORK_SECTION_NETWORK_ACCESSIBILITY_LABEL}, + {"networkSectionProxy", IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY}, + {"networkSectionProxyExpandA11yLabel", + IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY_ACCESSIBILITY_LABEL}, + {"networkShared", IDS_SETTINGS_INTERNET_NETWORK_SHARED}, + {"networkVpnBuiltin", IDS_NETWORK_TYPE_VPN_BUILTIN}, + {"networkOutOfRange", IDS_SETTINGS_INTERNET_WIFI_NETWORK_OUT_OF_RANGE}, + {"cellularContactSpecificCarrier", + IDS_SETTINGS_INTERNET_CELLULAR_CONTACT_SPECIFIC_CARRIER}, + {"cellularContactDefaultCarrier", + IDS_SETTINGS_INTERNET_CELLULAR_CONTACT_DEFAULT_CARRIER}, + {"tetherPhoneOutOfRange", + IDS_SETTINGS_INTERNET_TETHER_PHONE_OUT_OF_RANGE}, + {"gmscoreNotificationsTitle", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_TITLE}, + {"gmscoreNotificationsOneDeviceSubtitle", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_ONE_DEVICE_SUBTITLE}, + {"gmscoreNotificationsTwoDevicesSubtitle", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_TWO_DEVICES_SUBTITLE}, + {"gmscoreNotificationsManyDevicesSubtitle", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_MANY_DEVICES_SUBTITLE}, + {"gmscoreNotificationsFirstStep", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_FIRST_STEP}, + {"gmscoreNotificationsSecondStep", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_SECOND_STEP}, + {"gmscoreNotificationsThirdStep", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_THIRD_STEP}, + {"gmscoreNotificationsFourthStep", + IDS_SETTINGS_INTERNET_GMSCORE_NOTIFICATIONS_FOURTH_STEP}, + {"tetherConnectionDialogTitle", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DIALOG_TITLE}, + {"tetherConnectionAvailableDeviceTitle", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_AVAILABLE_DEVICE_TITLE}, + {"tetherConnectionBatteryPercentage", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_BATTERY_PERCENTAGE}, + {"tetherConnectionExplanation", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_EXPLANATION}, + {"tetherConnectionCarrierWarning", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_CARRIER_WARNING}, + {"tetherConnectionDescriptionTitle", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_TITLE}, + {"tetherConnectionDescriptionMobileData", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_MOBILE_DATA}, + {"tetherConnectionDescriptionBattery", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_BATTERY}, + {"tetherConnectionDescriptionWiFi", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_DESCRIPTION_WIFI}, + {"tetherConnectionNotNowButton", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_NOT_NOW_BUTTON}, + {"tetherConnectionConnectButton", + IDS_SETTINGS_INTERNET_TETHER_CONNECTION_CONNECT_BUTTON}, + {"tetherEnableBluetooth", IDS_ENABLE_BLUETOOTH}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + chromeos::network_element::AddLocalizedStrings(html_source); + chromeos::network_element::AddOncLocalizedStrings(html_source); + chromeos::network_element::AddDetailsLocalizedStrings(html_source); + chromeos::network_element::AddConfigLocalizedStrings(html_source); + chromeos::network_element::AddErrorLocalizedStrings(html_source); + + html_source->AddString("networkGoogleNameserversLearnMoreUrl", + chrome::kGoogleNameserversLearnMoreURL); + html_source->AddString( + "internetNoNetworksMobileData", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_INTERNET_LOOKING_FOR_MOBILE_NETWORK, + GetHelpUrlWithBoard(chrome::kInstantTetheringLearnMoreURL))); +} + +void InternetStringsProvider::OnDeviceStateListChanged() { + FetchDeviceList(); +} + +void InternetStringsProvider::FetchDeviceList() { + cros_network_config_->GetDeviceStateList(base::BindOnce( + &InternetStringsProvider::OnDeviceList, base::Unretained(this))); +} + +void InternetStringsProvider::OnDeviceList( + std::vector<network_config::mojom::DeviceStatePropertiesPtr> devices) { + // Start with no search tags. + delegate()->RemoveSearchTags(GetEthernetSearchConcepts()); + delegate()->RemoveSearchTags(GetWifiSearchConcepts()); + + // Add a search tag each time we see a device type. + for (const auto& device : devices) { + if (device->type == network_config::mojom::NetworkType::kEthernet) + delegate()->AddSearchTags(GetEthernetSearchConcepts()); + else if (device->type == network_config::mojom::NetworkType::kWiFi) + delegate()->AddSearchTags(GetWifiSearchConcepts()); + } +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.h b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.h new file mode 100644 index 00000000000..4a0e2a2511e --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.h @@ -0,0 +1,59 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_STRINGS_PROVIDER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_STRINGS_PROVIDER_H_ + +#include <vector> + +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.h" +#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" + +class Profile; + +namespace content { +class WebUIDataSource; +} // namespace content + +namespace chromeos { +namespace settings { + +class InternetStringsProvider + : public OsSettingsPerPageStringsProvider, + public network_config::mojom::CrosNetworkConfigObserver { + public: + InternetStringsProvider(Profile* profile, Delegate* per_page_delegate); + ~InternetStringsProvider() override; + + private: + // OsSettingsPerPageStringsProvider: + void AddUiStrings(content::WebUIDataSource* html_source) const override; + + // network_config::mojom::CrosNetworkConfigObserver: + void OnActiveNetworksChanged( + std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) + override {} + void OnNetworkStateChanged( + chromeos::network_config::mojom::NetworkStatePropertiesPtr network) + override {} + void OnNetworkStateListChanged() override {} + void OnVpnProvidersChanged() override {} + void OnNetworkCertificatesChanged() override {} + void OnDeviceStateListChanged() override; + + void FetchDeviceList(); + void OnDeviceList( + std::vector<network_config::mojom::DeviceStatePropertiesPtr> devices); + + mojo::Receiver<network_config::mojom::CrosNetworkConfigObserver> receiver_{ + this}; + mojo::Remote<network_config::mojom::CrosNetworkConfig> cros_network_config_; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_STRINGS_PROVIDER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h index 8ee0b692ac1..a103760bd1e 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h @@ -13,7 +13,7 @@ #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chromeos/components/multidevice/remote_device_ref.h" #include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client.h" -#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" +#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom-forward.h" #include "components/prefs/pref_change_registrar.h" class PrefService; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc new file mode 100644 index 00000000000..653188ccf20 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.cc @@ -0,0 +1,2136 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h" + +#include "ash/public/cpp/ash_features.h" +#include "ash/public/mojom/assistant_state_controller.mojom.h" +#include "base/command_line.h" +#include "base/feature_list.h" +#include "base/i18n/number_formatting.h" +#include "base/no_destructor.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/system/sys_info.h" +#include "build/branding_buildflags.h" +#include "build/build_config.h" +#include "build/buildflag.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_process_platform_part.h" +#include "chrome/browser/chromeos/account_manager/account_manager_util.h" +#include "chrome/browser/chromeos/arc/arc_util.h" +#include "chrome/browser/chromeos/assistant/assistant_util.h" +#include "chrome/browser/chromeos/crostini/crostini_features.h" +#include "chrome/browser/chromeos/crostini/crostini_util.h" +#include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager.h" +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" +#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/policy/profile_policy_connector.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/supervised_user/supervised_user_service.h" +#include "chrome/browser/supervised_user/supervised_user_service_factory.h" +#include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_utils.h" +#include "chrome/browser/ui/webui/chromeos/bluetooth_dialog_localized_strings_provider.h" +#include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h" +#include "chrome/browser/ui/webui/chromeos/smb_shares/smb_shares_localized_strings_provider.h" +#include "chrome/browser/ui/webui/management_ui.h" +#include "chrome/browser/ui/webui/policy_indicator_localized_strings_provider.h" +#include "chrome/browser/ui/webui/settings/chromeos/internet_strings_provider.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" +#include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h" +#include "chrome/browser/ui/webui/webui_util.h" +#include "chrome/common/chrome_features.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/grit/chromium_strings.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/grit/locale_settings.h" +#include "chrome/services/local_search_service/public/mojom/types.mojom.h" +#include "chromeos/constants/chromeos_features.h" +#include "chromeos/constants/chromeos_switches.h" +#include "chromeos/services/assistant/public/features.h" +#include "chromeos/services/multidevice_setup/public/cpp/url_provider.h" +#include "chromeos/strings/grit/chromeos_strings.h" +#include "components/google/core/common/google_util.h" +#include "components/prefs/pref_service.h" +#include "components/strings/grit/components_strings.h" +#include "components/version_ui/version_ui_constants.h" +#include "content/public/browser/web_ui_data_source.h" +#include "content/public/common/content_features.h" +#include "content/public/common/content_switches.h" +#include "device/bluetooth/strings/grit/bluetooth_strings.h" +#include "media/base/media_switches.h" +#include "ui/accessibility/accessibility_switches.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/webui/web_ui_util.h" +#include "ui/chromeos/devicetype_utils.h" +#include "ui/chromeos/events/keyboard_layout_util.h" +#include "ui/display/display_features.h" +#include "ui/display/display_switches.h" +#include "ui/display/manager/touch_device_manager.h" + +namespace chromeos { +namespace settings { +namespace { + +std::vector<local_search_service::mojom::DataPtr> ConceptVectorToDataPtrVector( + const std::vector<SearchConcept>& tags_group) { + std::vector<local_search_service::mojom::DataPtr> data_list; + + for (const auto& concept : tags_group) { + std::vector<base::string16> search_tags; + + // Add the canonical tag. + search_tags.push_back( + l10n_util::GetStringUTF16(concept.canonical_message_id)); + + // Add all alternate tags. + for (size_t i = 0; i < SearchConcept::kMaxAltTagsPerConcept; ++i) { + int curr_alt_tag = concept.alt_tag_ids[i]; + if (curr_alt_tag == SearchConcept::kAltTagEnd) + break; + search_tags.push_back(l10n_util::GetStringUTF16(curr_alt_tag)); + } + + // Note: A stringified version of the canonical tag message ID is used as + // the identifier for this search data. + data_list.push_back(local_search_service::mojom::Data::New( + base::NumberToString(concept.canonical_message_id), search_tags)); + } + + return data_list; +} + +// Generates a Google Help URL which includes a "board type" parameter. Some +// help pages need to be adjusted depending on the type of CrOS device that is +// accessing the page. +base::string16 GetHelpUrlWithBoard(const std::string& original_url) { + return base::ASCIIToUTF16(original_url + + "&b=" + base::SysInfo::GetLsbReleaseBoard()); +} + +bool IsDeviceManaged() { + policy::BrowserPolicyConnectorChromeOS* connector = + g_browser_process->platform_part()->browser_policy_connector_chromeos(); + return connector->IsEnterpriseManaged(); +} + +bool IsProfileManaged(Profile* profile) { + return profile->GetProfilePolicyConnector()->IsManaged(); +} + +void AddCommonStrings(content::WebUIDataSource* html_source, Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"add", IDS_ADD}, + {"advancedPageTitle", IDS_SETTINGS_ADVANCED}, + {"back", IDS_ACCNAME_BACK}, + {"basicPageTitle", IDS_SETTINGS_BASIC}, + {"cancel", IDS_CANCEL}, + {"clear", IDS_SETTINGS_CLEAR}, + {"close", IDS_CLOSE}, + {"confirm", IDS_CONFIRM}, + {"continue", IDS_SETTINGS_CONTINUE}, + {"controlledByExtension", IDS_SETTINGS_CONTROLLED_BY_EXTENSION}, + {"custom", IDS_SETTINGS_CUSTOM}, + {"delete", IDS_SETTINGS_DELETE}, + {"deviceOff", IDS_SETTINGS_DEVICE_OFF}, + {"deviceOn", IDS_SETTINGS_DEVICE_ON}, + {"disable", IDS_DISABLE}, + {"done", IDS_DONE}, + {"edit", IDS_SETTINGS_EDIT}, + {"extensionsLinkTooltip", IDS_SETTINGS_MENU_EXTENSIONS_LINK_TOOLTIP}, + {"learnMore", IDS_LEARN_MORE}, + {"menu", IDS_MENU}, + {"menuButtonLabel", IDS_SETTINGS_MENU_BUTTON_LABEL}, + {"moreActions", IDS_SETTINGS_MORE_ACTIONS}, + {"ok", IDS_OK}, + {"restart", IDS_SETTINGS_RESTART}, + {"save", IDS_SAVE}, + {"searchResultBubbleText", IDS_SEARCH_RESULT_BUBBLE_TEXT}, + {"searchResultsBubbleText", IDS_SEARCH_RESULTS_BUBBLE_TEXT}, + {"settings", IDS_SETTINGS_SETTINGS}, + {"settingsAltPageTitle", IDS_SETTINGS_ALT_PAGE_TITLE}, + {"subpageArrowRoleDescription", IDS_SETTINGS_SUBPAGE_BUTTON}, + {"notValidWebAddress", IDS_SETTINGS_NOT_VALID_WEB_ADDRESS}, + {"notValidWebAddressForContentType", + IDS_SETTINGS_NOT_VALID_WEB_ADDRESS_FOR_CONTENT_TYPE}, + + // Common font related strings shown in a11y and appearance sections. + {"quickBrownFox", IDS_SETTINGS_QUICK_BROWN_FOX}, + {"verySmall", IDS_SETTINGS_VERY_SMALL_FONT}, + {"small", IDS_SETTINGS_SMALL_FONT}, + {"medium", IDS_SETTINGS_MEDIUM_FONT}, + {"large", IDS_SETTINGS_LARGE_FONT}, + {"veryLarge", IDS_SETTINGS_VERY_LARGE_FONT}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddBoolean( + "isGuest", + user_manager::UserManager::Get()->IsLoggedInAsGuest() || + user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()); + + html_source->AddBoolean("isSupervised", profile->IsSupervised()); +} + +void AddA11yStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"a11yPageTitle", IDS_SETTINGS_ACCESSIBILITY}, + {"a11yWebStore", IDS_SETTINGS_ACCESSIBILITY_WEB_STORE}, + {"moreFeaturesLinkDescription", + IDS_SETTINGS_MORE_FEATURES_LINK_DESCRIPTION}, + {"accessibleImageLabelsTitle", + IDS_SETTINGS_ACCESSIBLE_IMAGE_LABELS_TITLE}, + {"accessibleImageLabelsSubtitle", + IDS_SETTINGS_ACCESSIBLE_IMAGE_LABELS_SUBTITLE}, + {"settingsSliderRoleDescription", + IDS_SETTINGS_SLIDER_MIN_MAX_ARIA_ROLE_DESCRIPTION}, + {"manageAccessibilityFeatures", + IDS_SETTINGS_ACCESSIBILITY_MANAGE_ACCESSIBILITY_FEATURES}, + {"optionsInMenuLabel", IDS_SETTINGS_OPTIONS_IN_MENU_LABEL}, + {"largeMouseCursorLabel", IDS_SETTINGS_LARGE_MOUSE_CURSOR_LABEL}, + {"largeMouseCursorSizeLabel", IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_LABEL}, + {"largeMouseCursorSizeDefaultLabel", + IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_DEFAULT_LABEL}, + {"largeMouseCursorSizeLargeLabel", + IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_LARGE_LABEL}, + {"highContrastLabel", IDS_SETTINGS_HIGH_CONTRAST_LABEL}, + {"stickyKeysLabel", IDS_SETTINGS_STICKY_KEYS_LABEL}, + {"chromeVoxLabel", IDS_SETTINGS_CHROMEVOX_LABEL}, + {"chromeVoxOptionsLabel", IDS_SETTINGS_CHROMEVOX_OPTIONS_LABEL}, + {"screenMagnifierLabel", IDS_SETTINGS_SCREEN_MAGNIFIER_LABEL}, + {"screenMagnifierZoomLabel", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_LABEL}, + {"dockedMagnifierLabel", IDS_SETTINGS_DOCKED_MAGNIFIER_LABEL}, + {"dockedMagnifierZoomLabel", IDS_SETTINGS_DOCKED_MAGNIFIER_ZOOM_LABEL}, + {"screenMagnifierZoom2x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_2_X}, + {"screenMagnifierZoom4x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_4_X}, + {"screenMagnifierZoom6x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_6_X}, + {"screenMagnifierZoom8x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_8_X}, + {"screenMagnifierZoom10x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_10_X}, + {"screenMagnifierZoom12x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_12_X}, + {"screenMagnifierZoom14x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_14_X}, + {"screenMagnifierZoom16x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_16_X}, + {"screenMagnifierZoom18x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_18_X}, + {"screenMagnifierZoom20x", IDS_SETTINGS_SCREEN_MAGNIFIER_ZOOM_20_X}, + {"tapDraggingLabel", IDS_SETTINGS_TAP_DRAGGING_LABEL}, + {"clickOnStopLabel", IDS_SETTINGS_CLICK_ON_STOP_LABEL}, + {"delayBeforeClickLabel", IDS_SETTINGS_DELAY_BEFORE_CLICK_LABEL}, + {"delayBeforeClickExtremelyShort", + IDS_SETTINGS_DELAY_BEFORE_CLICK_EXTREMELY_SHORT}, + {"delayBeforeClickVeryShort", IDS_SETTINGS_DELAY_BEFORE_CLICK_VERY_SHORT}, + {"delayBeforeClickShort", IDS_SETTINGS_DELAY_BEFORE_CLICK_SHORT}, + {"delayBeforeClickLong", IDS_SETTINGS_DELAY_BEFORE_CLICK_LONG}, + {"delayBeforeClickVeryLong", IDS_SETTINGS_DELAY_BEFORE_CLICK_VERY_LONG}, + {"autoclickRevertToLeftClick", + IDS_SETTINGS_AUTOCLICK_REVERT_TO_LEFT_CLICK}, + {"autoclickStabilizeCursorPosition", + IDS_SETTINGS_AUTOCLICK_STABILIZE_CURSOR_POSITION}, + {"autoclickMovementThresholdLabel", + IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LABEL}, + {"autoclickMovementThresholdExtraSmall", + IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_SMALL}, + {"autoclickMovementThresholdSmall", + IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_SMALL}, + {"autoclickMovementThresholdDefault", + IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_DEFAULT}, + {"autoclickMovementThresholdLarge", + IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_LARGE}, + {"autoclickMovementThresholdExtraLarge", + IDS_SETTINGS_AUTOCLICK_MOVEMENT_THRESHOLD_EXTRA_LARGE}, + {"dictationDescription", + IDS_SETTINGS_ACCESSIBILITY_DICTATION_DESCRIPTION}, + {"dictationLabel", IDS_SETTINGS_ACCESSIBILITY_DICTATION_LABEL}, + {"onScreenKeyboardLabel", IDS_SETTINGS_ON_SCREEN_KEYBOARD_LABEL}, + {"monoAudioLabel", IDS_SETTINGS_MONO_AUDIO_LABEL}, + {"startupSoundLabel", IDS_SETTINGS_STARTUP_SOUND_LABEL}, + {"a11yExplanation", IDS_SETTINGS_ACCESSIBILITY_EXPLANATION}, + {"caretHighlightLabel", + IDS_SETTINGS_ACCESSIBILITY_CARET_HIGHLIGHT_DESCRIPTION}, + {"cursorHighlightLabel", + IDS_SETTINGS_ACCESSIBILITY_CURSOR_HIGHLIGHT_DESCRIPTION}, + {"focusHighlightLabel", + IDS_SETTINGS_ACCESSIBILITY_FOCUS_HIGHLIGHT_DESCRIPTION}, + {"selectToSpeakTitle", IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_TITLE}, + {"selectToSpeakDisabledDescription", + IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DISABLED_DESCRIPTION}, + {"selectToSpeakDescription", + IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION}, + {"selectToSpeakDescriptionWithoutKeyboard", + IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_DESCRIPTION_WITHOUT_KEYBOARD}, + {"selectToSpeakOptionsLabel", + IDS_SETTINGS_ACCESSIBILITY_SELECT_TO_SPEAK_OPTIONS_LABEL}, + {"switchAccessLabel", + IDS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_DESCRIPTION}, + {"switchAccessOptionsLabel", + IDS_SETTINGS_ACCESSIBILITY_SWITCH_ACCESS_OPTIONS_LABEL}, + {"manageSwitchAccessSettings", + IDS_SETTINGS_MANAGE_SWITCH_ACCESS_SETTINGS}, + {"switchAssignmentHeading", IDS_SETTINGS_SWITCH_ASSIGNMENT_HEADING}, + {"switchAssignOptionNone", IDS_SETTINGS_SWITCH_ASSIGN_OPTION_NONE}, + {"switchAssignOptionSpace", IDS_SETTINGS_SWITCH_ASSIGN_OPTION_SPACE}, + {"switchAssignOptionEnter", IDS_SETTINGS_SWITCH_ASSIGN_OPTION_ENTER}, + {"assignSelectSwitchLabel", IDS_SETTINGS_ASSIGN_SELECT_SWITCH_LABEL}, + {"assignNextSwitchLabel", IDS_SETTINGS_ASSIGN_NEXT_SWITCH_LABEL}, + {"assignPreviousSwitchLabel", IDS_SETTINGS_ASSIGN_PREVIOUS_SWITCH_LABEL}, + {"switchAccessAutoScanHeading", + IDS_SETTINGS_SWITCH_ACCESS_AUTO_SCAN_HEADING}, + {"switchAccessAutoScanLabel", IDS_SETTINGS_SWITCH_ACCESS_AUTO_SCAN_LABEL}, + {"switchAccessAutoScanSpeedLabel", + IDS_SETTINGS_SWITCH_ACCESS_AUTO_SCAN_SPEED_LABEL}, + {"switchAccessAutoScanKeyboardSpeedLabel", + IDS_SETTINGS_SWITCH_ACCESS_AUTO_SCAN_KEYBOARD_SPEED_LABEL}, + {"durationInSeconds", IDS_SETTINGS_DURATION_IN_SECONDS}, + {"manageAccessibilityFeatures", + IDS_SETTINGS_ACCESSIBILITY_MANAGE_ACCESSIBILITY_FEATURES}, + {"textToSpeechHeading", + IDS_SETTINGS_ACCESSIBILITY_TEXT_TO_SPEECH_HEADING}, + {"displayHeading", IDS_SETTINGS_ACCESSIBILITY_DISPLAY_HEADING}, + {"displaySettingsTitle", + IDS_SETTINGS_ACCESSIBILITY_DISPLAY_SETTINGS_TITLE}, + {"displaySettingsDescription", + IDS_SETTINGS_ACCESSIBILITY_DISPLAY_SETTINGS_DESCRIPTION}, + {"appearanceSettingsTitle", + IDS_SETTINGS_ACCESSIBILITY_APPEARANCE_SETTINGS_TITLE}, + {"appearanceSettingsDescription", + IDS_SETTINGS_ACCESSIBILITY_APPEARANCE_SETTINGS_DESCRIPTION}, + {"keyboardAndTextInputHeading", + IDS_SETTINGS_ACCESSIBILITY_KEYBOARD_AND_TEXT_INPUT_HEADING}, + {"keyboardSettingsTitle", + IDS_SETTINGS_ACCESSIBILITY_KEYBOARD_SETTINGS_TITLE}, + {"keyboardSettingsDescription", + IDS_SETTINGS_ACCESSIBILITY_KEYBOARD_SETTINGS_DESCRIPTION}, + {"mouseAndTouchpadHeading", + IDS_SETTINGS_ACCESSIBILITY_MOUSE_AND_TOUCHPAD_HEADING}, + {"mouseSettingsTitle", IDS_SETTINGS_ACCESSIBILITY_MOUSE_SETTINGS_TITLE}, + {"mouseSettingsDescription", + IDS_SETTINGS_ACCESSIBILITY_MOUSE_SETTINGS_DESCRIPTION}, + {"audioAndCaptionsHeading", + IDS_SETTINGS_ACCESSIBILITY_AUDIO_AND_CAPTIONS_HEADING}, + {"additionalFeaturesTitle", + IDS_SETTINGS_ACCESSIBILITY_ADDITIONAL_FEATURES_TITLE}, + {"manageTtsSettings", IDS_SETTINGS_MANAGE_TTS_SETTINGS}, + {"ttsSettingsLinkDescription", IDS_SETTINGS_TTS_LINK_DESCRIPTION}, + {"textToSpeechVoices", IDS_SETTINGS_TEXT_TO_SPEECH_VOICES}, + {"textToSpeechNoVoicesMessage", + IDS_SETTINGS_TEXT_TO_SPEECH_NO_VOICES_MESSAGE}, + {"textToSpeechMoreLanguages", IDS_SETTINGS_TEXT_TO_SPEECH_MORE_LANGUAGES}, + {"textToSpeechProperties", IDS_SETTINGS_TEXT_TO_SPEECH_PROPERTIES}, + {"textToSpeechRate", IDS_SETTINGS_TEXT_TO_SPEECH_RATE}, + {"textToSpeechRateMinimumLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_RATE_MINIMUM_LABEL}, + {"textToSpeechRateMaximumLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_RATE_MAXIMUM_LABEL}, + {"textToSpeechPitch", IDS_SETTINGS_TEXT_TO_SPEECH_PITCH}, + {"textToSpeechPitchMinimumLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_PITCH_MINIMUM_LABEL}, + {"textToSpeechPitchMaximumLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_PITCH_MAXIMUM_LABEL}, + {"textToSpeechVolume", IDS_SETTINGS_TEXT_TO_SPEECH_VOLUME}, + {"textToSpeechVolumeMinimumLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_VOLUME_MINIMUM_LABEL}, + {"textToSpeechVolumeMaximumLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_VOLUME_MAXIMUM_LABEL}, + {"percentage", IDS_SETTINGS_PERCENTAGE}, + {"defaultPercentage", IDS_SETTINGS_DEFAULT_PERCENTAGE}, + {"textToSpeechPreviewHeading", + IDS_SETTINGS_TEXT_TO_SPEECH_PREVIEW_HEADING}, + {"textToSpeechPreviewInputLabel", + IDS_SETTINGS_TEXT_TO_SPEECH_PREVIEW_INPUT_LABEL}, + {"textToSpeechPreviewInput", IDS_SETTINGS_TEXT_TO_SPEECH_PREVIEW_INPUT}, + {"textToSpeechPreviewVoice", IDS_SETTINGS_TEXT_TO_SPEECH_PREVIEW_VOICE}, + {"textToSpeechPreviewPlay", IDS_SETTINGS_TEXT_TO_SPEECH_PREVIEW_PLAY}, + {"textToSpeechEngines", IDS_SETTINGS_TEXT_TO_SPEECH_ENGINES}, + {"tabletModeShelfNavigationButtonsSettingLabel", + IDS_SETTINGS_A11Y_TABLET_MODE_SHELF_BUTTONS_LABEL}, + {"tabletModeShelfNavigationButtonsSettingDescription", + IDS_SETTINGS_A11Y_TABLET_MODE_SHELF_BUTTONS_DESCRIPTION}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString("accountManagerLearnMoreUrl", + chrome::kAccountManagerLearnMoreURL); + html_source->AddString("a11yLearnMoreUrl", + chrome::kChromeAccessibilityHelpURL); + + base::CommandLine& cmd = *base::CommandLine::ForCurrentProcess(); + html_source->AddBoolean( + "showExperimentalAccessibilitySwitchAccess", + cmd.HasSwitch(::switches::kEnableExperimentalAccessibilitySwitchAccess)); + + html_source->AddBoolean( + "showExperimentalAccessibilitySwitchAccessImprovedTextInput", + cmd.HasSwitch( + ::switches::kEnableExperimentalAccessibilitySwitchAccessText)); + + html_source->AddBoolean("showExperimentalA11yLabels", + base::FeatureList::IsEnabled( + ::features::kExperimentalAccessibilityLabels)); + + html_source->AddBoolean( + "showTabletModeShelfNavigationButtonsSettings", + ash::features::IsHideShelfControlsInTabletModeEnabled()); + + html_source->AddString("tabletModeShelfNavigationButtonsLearnMoreUrl", + chrome::kTabletModeGesturesLearnMoreURL); + + html_source->AddBoolean("enableLiveCaption", + base::FeatureList::IsEnabled(media::kLiveCaption)); + + ::settings::AddCaptionSubpageStrings(html_source); +} + +void AddLanguagesStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"orderLanguagesInstructions", + IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_ORDERING_INSTRUCTIONS}, + {"osLanguagesPageTitle", IDS_OS_SETTINGS_LANGUAGES_AND_INPUT_PAGE_TITLE}, + {"osLanguagesListTitle", IDS_OS_SETTINGS_LANGUAGES_LIST_TITLE}, + {"inputMethodsListTitle", + IDS_SETTINGS_LANGUAGES_INPUT_METHODS_LIST_TITLE}, + {"inputMethodEnabled", IDS_SETTINGS_LANGUAGES_INPUT_METHOD_ENABLED}, + {"inputMethodsExpandA11yLabel", + IDS_SETTINGS_LANGUAGES_INPUT_METHODS_EXPAND_ACCESSIBILITY_LABEL}, + {"inputMethodsManagedbyPolicy", + IDS_SETTINGS_LANGUAGES_INPUT_METHODS_MANAGED_BY_POLICY}, + {"manageInputMethods", IDS_SETTINGS_LANGUAGES_INPUT_METHODS_MANAGE}, + {"manageInputMethodsPageTitle", + IDS_SETTINGS_LANGUAGES_MANAGE_INPUT_METHODS_TITLE}, + {"showImeMenu", IDS_SETTINGS_LANGUAGES_SHOW_IME_MENU}, + {"displayLanguageRestart", + IDS_SETTINGS_LANGUAGES_RESTART_TO_DISPLAY_LANGUAGE}, + {"moveDown", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_DOWN}, + {"displayInThisLanguage", + IDS_SETTINGS_LANGUAGES_DISPLAY_IN_THIS_LANGUAGE}, + {"searchLanguages", IDS_SETTINGS_LANGUAGE_SEARCH}, + {"addLanguagesDialogTitle", + IDS_SETTINGS_LANGUAGES_MANAGE_LANGUAGES_TITLE}, + {"moveToTop", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_TO_TOP}, + {"isDisplayedInThisLanguage", + IDS_SETTINGS_LANGUAGES_IS_DISPLAYED_IN_THIS_LANGUAGE}, + {"removeLanguage", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_REMOVE}, + {"addLanguages", IDS_SETTINGS_LANGUAGES_LANGUAGES_ADD}, + {"moveUp", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_UP}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString( + "languagesLearnMoreURL", + base::ASCIIToUTF16(chrome::kLanguageSettingsLearnMoreUrl)); +} + +void AddPersonalizationStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"ambientModeTitle", IDS_OS_SETTINGS_AMBIENT_MODE_TITLE}, + {"ambientModeEnabled", IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED}, + {"ambientModeDisabled", IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED}, + {"ambientModeOn", IDS_OS_SETTINGS_AMBIENT_MODE_ON}, + {"ambientModeOff", IDS_OS_SETTINGS_AMBIENT_MODE_OFF}, + {"ambientModeTopicSourceTitle", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE}, + {"ambientModeTopicSourceGooglePhotos", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS}, + {"ambientModeTopicSourceArtGallery", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY}, + {"changePictureTitle", IDS_OS_SETTINGS_CHANGE_PICTURE_TITLE}, + {"openWallpaperApp", IDS_OS_SETTINGS_OPEN_WALLPAPER_APP}, + {"personalizationPageTitle", IDS_OS_SETTINGS_PERSONALIZATION}, + {"setWallpaper", IDS_OS_SETTINGS_SET_WALLPAPER}, + {"takePhoto", IDS_SETTINGS_CHANGE_PICTURE_TAKE_PHOTO}, + {"captureVideo", IDS_SETTINGS_CHANGE_PICTURE_CAPTURE_VIDEO}, + {"discardPhoto", IDS_SETTINGS_CHANGE_PICTURE_DISCARD_PHOTO}, + {"previewAltText", IDS_SETTINGS_CHANGE_PICTURE_PREVIEW_ALT}, + {"switchModeToVideo", IDS_SETTINGS_CHANGE_PICTURE_SWITCH_MODE_TO_VIDEO}, + {"profilePhoto", IDS_SETTINGS_CHANGE_PICTURE_PROFILE_PHOTO}, + {"changePicturePageDescription", IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TEXT}, + {"switchModeToCamera", IDS_SETTINGS_CHANGE_PICTURE_SWITCH_MODE_TO_CAMERA}, + {"chooseFile", IDS_SETTINGS_CHANGE_PICTURE_CHOOSE_FILE}, + {"oldPhoto", IDS_SETTINGS_CHANGE_PICTURE_OLD_PHOTO}, + {"oldVideo", IDS_SETTINGS_CHANGE_PICTURE_OLD_VIDEO}, + {"authorCreditText", IDS_SETTINGS_CHANGE_PICTURE_AUTHOR_CREDIT_TEXT}, + {"photoCaptureAccessibleText", + IDS_SETTINGS_PHOTO_CAPTURE_ACCESSIBLE_TEXT}, + {"photoDiscardAccessibleText", + IDS_SETTINGS_PHOTO_DISCARD_ACCESSIBLE_TEXT}, + {"photoModeAccessibleText", IDS_SETTINGS_PHOTO_MODE_ACCESSIBLE_TEXT}, + {"videoModeAccessibleText", IDS_SETTINGS_VIDEO_MODE_ACCESSIBLE_TEXT}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddBoolean( + "changePictureVideoModeEnabled", + base::FeatureList::IsEnabled(::features::kChangePictureVideoMode)); + html_source->AddBoolean("isAmbientModeEnabled", + chromeos::features::IsAmbientModeEnabled()); +} + +void AddFingerprintListStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"lockScreenAddFingerprint", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_ADD_FINGERPRINT_BUTTON}, + {"lockScreenRegisteredFingerprints", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_REGISTERED_FINGERPRINTS_LABEL}, + {"lockScreenFingerprintWarning", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_LESS_SECURE}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddSetupPinDialogStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"configurePinChoosePinTitle", + IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CHOOSE_PIN_TITLE}, + {"configurePinConfirmPinTitle", + IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_CONFIRM_PIN_TITLE}, + {"configurePinMismatched", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_MISMATCHED}, + {"configurePinTooShort", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_SHORT}, + {"configurePinTooLong", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_TOO_LONG}, + {"configurePinWeakPin", IDS_SETTINGS_PEOPLE_CONFIGURE_PIN_WEAK_PIN}, + {"pinKeyboardPlaceholderPin", IDS_PIN_KEYBOARD_HINT_TEXT_PIN}, + {"pinKeyboardPlaceholderPinPassword", + IDS_PIN_KEYBOARD_HINT_TEXT_PIN_PASSWORD}, + {"pinKeyboardDeleteAccessibleName", + IDS_PIN_KEYBOARD_DELETE_ACCESSIBLE_NAME}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + // Format numbers to be used on the pin keyboard. + for (int j = 0; j <= 9; j++) { + html_source->AddString("pinKeyboard" + base::NumberToString(j), + base::FormatNumber(int64_t{j})); + } +} + +void AddSetupFingerprintDialogStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"configureFingerprintTitle", IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_TITLE}, + {"configureFingerprintAddAnotherButton", + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_ADD_ANOTHER_BUTTON}, + {"configureFingerprintInstructionReadyStep", + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_READY}, + {"configureFingerprintLiftFinger", + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_LIFT_FINGER}, + {"configureFingerprintTryAgain", + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_TRY_AGAIN}, + {"configureFingerprintImmobile", + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_FINGER_IMMOBILE}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddFingerprintStrings(content::WebUIDataSource* html_source) { + int instruction_id, aria_label_id; + using FingerprintLocation = chromeos::quick_unlock::FingerprintLocation; + switch (chromeos::quick_unlock::GetFingerprintLocation()) { + case FingerprintLocation::TABLET_POWER_BUTTON: + instruction_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_POWER_BUTTON; + aria_label_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_POWER_BUTTON_ARIA_LABEL; + break; + case FingerprintLocation::KEYBOARD_BOTTOM_LEFT: + instruction_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD; + aria_label_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_BOTTOM_LEFT_ARIA_LABEL; + break; + case FingerprintLocation::KEYBOARD_BOTTOM_RIGHT: + instruction_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD; + aria_label_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_BOTTOM_RIGHT_ARIA_LABEL; + break; + case FingerprintLocation::KEYBOARD_TOP_RIGHT: + instruction_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD; + aria_label_id = + IDS_SETTINGS_ADD_FINGERPRINT_DIALOG_INSTRUCTION_LOCATE_SCANNER_KEYBOARD_TOP_RIGHT_ARIA_LABEL; + break; + } + html_source->AddLocalizedString( + "configureFingerprintInstructionLocateScannerStep", instruction_id); + html_source->AddLocalizedString("configureFingerprintScannerStepAriaLabel", + aria_label_id); +} + +void AddAccountManagerPageStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"accountManagerDescription", IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION}, + {"accountManagerChildDescription", + IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_DESCRIPTION}, + {"accountListHeader", IDS_SETTINGS_ACCOUNT_MANAGER_LIST_HEADER}, + {"accountManagerPrimaryAccountTooltip", + IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP}, + {"accountManagerEducationAccountLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_EDUCATION_ACCOUNT}, + {"removeAccountLabel", IDS_SETTINGS_ACCOUNT_MANAGER_REMOVE_ACCOUNT_LABEL}, + {"addAccountLabel", IDS_SETTINGS_ACCOUNT_MANAGER_ADD_ACCOUNT_LABEL}, + {"addSchoolAccountLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_ADD_SCHOOL_ACCOUNT_LABEL}, + {"accountManagerSecondaryAccountsDisabledText", + IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_TEXT}, + {"accountManagerSecondaryAccountsDisabledChildText", + IDS_SETTINGS_ACCOUNT_MANAGER_SECONDARY_ACCOUNTS_DISABLED_CHILD_TEXT}, + {"accountManagerSignedOutAccountName", + IDS_SETTINGS_ACCOUNT_MANAGER_SIGNED_OUT_ACCOUNT_PLACEHOLDER}, + {"accountManagerUnmigratedAccountName", + IDS_SETTINGS_ACCOUNT_MANAGER_UNMIGRATED_ACCOUNT_PLACEHOLDER}, + {"accountManagerMigrationLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_MIGRATION_LABEL}, + {"accountManagerReauthenticationLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_LABEL}, + {"accountManagerMigrationTooltip", + IDS_SETTINGS_ACCOUNT_MANAGER_MIGRATION_TOOLTIP}, + {"accountManagerReauthenticationTooltip", + IDS_SETTINGS_ACCOUNT_MANAGER_REAUTHENTICATION_TOOLTIP}, + {"accountManagerMoreActionsTooltip", + IDS_SETTINGS_ACCOUNT_MANAGER_MORE_ACTIONS_TOOLTIP}, + {"accountManagerManagedLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_MANAGED_ACCOUNT}, + {"accountManagerUnmanagedLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_MANAGEMENT_STATUS_UNMANAGED_ACCOUNT}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddSyncControlsStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"syncEverythingCheckboxLabel", + IDS_SETTINGS_SYNC_EVERYTHING_CHECKBOX_LABEL}, + {"wallpaperCheckboxLabel", IDS_OS_SETTINGS_WALLPAPER_CHECKBOX_LABEL}, + {"osSyncTurnOff", IDS_OS_SETTINGS_SYNC_TURN_OFF}, + {"osSyncSettingsCheckboxLabel", + IDS_OS_SETTINGS_SYNC_SETTINGS_CHECKBOX_LABEL}, + {"wifiConfigurationsCheckboxLabel", + IDS_SETTINGS_WIFI_CONFIGURATIONS_CHECKBOX_LABEL}, + {"osSyncAppsCheckboxLabel", IDS_OS_SETTINGS_SYNC_APPS_CHECKBOX_LABEL}, + {"osSyncTurnOn", IDS_OS_SETTINGS_SYNC_TURN_ON}, + {"osSyncFeatureLabel", IDS_OS_SETTINGS_SYNC_FEATURE_LABEL}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString( + "browserSettingsSyncSetupUrl", + base::StrCat({chrome::kChromeUISettingsURL, chrome::kSyncSetupSubPage})); +} + +void AddCrostiniStrings(content::WebUIDataSource* html_source, + Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"crostiniPageTitle", IDS_SETTINGS_CROSTINI_TITLE}, + {"crostiniPageLabel", IDS_SETTINGS_CROSTINI_LABEL}, + {"crostiniEnable", IDS_SETTINGS_TURN_ON}, + {"crostiniSharedPaths", IDS_SETTINGS_CROSTINI_SHARED_PATHS}, + {"crostiniSharedPathsListHeading", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING}, + {"crostiniSharedPathsInstructionsAdd", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD}, + {"crostiniSharedPathsInstructionsRemove", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE}, + {"crostiniSharedPathsRemoveSharing", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING}, + {"crostiniSharedPathsRemoveFailureDialogMessage", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE}, + {"crostiniSharedPathsRemoveFailureDialogTitle", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE}, + {"crostiniSharedPathsRemoveFailureTryAgain", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN}, + {"crostiniSharedPathsListEmptyMessage", + IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_EMPTY_MESSAGE}, + {"crostiniExportImportTitle", IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE}, + {"crostiniExport", IDS_SETTINGS_CROSTINI_EXPORT}, + {"crostiniExportLabel", IDS_SETTINGS_CROSTINI_EXPORT_LABEL}, + {"crostiniImport", IDS_SETTINGS_CROSTINI_IMPORT}, + {"crostiniImportLabel", IDS_SETTINGS_CROSTINI_IMPORT_LABEL}, + {"crostiniImportConfirmationDialogTitle", + IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_TITLE}, + {"crostiniImportConfirmationDialogMessage", + IDS_SETTINGS_CROSTINI_CONFIRM_IMPORT_DIALOG_WINDOW_MESSAGE}, + {"crostiniImportConfirmationDialogConfirmationButton", + IDS_SETTINGS_CROSTINI_IMPORT}, + {"crostiniRemoveButton", IDS_SETTINGS_CROSTINI_REMOVE_BUTTON}, + {"crostiniSharedUsbDevicesLabel", + IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LABEL}, + {"crostiniSharedUsbDevicesDescription", + IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_DESCRIPTION}, + {"crostiniSharedUsbDevicesExtraDescription", + IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION}, + {"crostiniSharedUsbDevicesListEmptyMessage", + IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE}, + {"crostiniArcAdbTitle", IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE}, + {"crostiniArcAdbDescription", IDS_SETTINGS_CROSTINI_ARC_ADB_DESCRIPTION}, + {"crostiniArcAdbLabel", IDS_SETTINGS_CROSTINI_ARC_ADB_LABEL}, + {"crostiniArcAdbRestartButton", + IDS_SETTINGS_CROSTINI_ARC_ADB_RESTART_BUTTON}, + {"crostiniArcAdbConfirmationTitleEnable", + IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_TITLE_ENABLE}, + {"crostiniArcAdbConfirmationTitleDisable", + IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_TITLE_DISABLE}, + {"crostiniContainerUpgrade", + IDS_SETTINGS_CROSTINI_CONTAINER_UPGRADE_MESSAGE}, + {"crostiniContainerUpgradeSubtext", + IDS_SETTINGS_CROSTINI_CONTAINER_UPGRADE_SUBTEXT}, + {"crostiniContainerUpgradeButton", + IDS_SETTINGS_CROSTINI_CONTAINER_UPGRADE_BUTTON}, + {"crostiniPortForwarding", IDS_SETTINGS_CROSTINI_PORT_FORWARDING}, + {"crostiniPortForwardingDescription", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_DESCRIPTION}, + {"crostiniPortForwardingNoPorts", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_NO_PORTS}, + {"crostiniPortForwardingTableTitle", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TABLE_TITLE}, + {"crostiniPortForwardingListPortNumber", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_PORT_NUMBER}, + {"crostiniPortForwardingListLabel", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_LIST_LABEL}, + {"crostiniPortForwardingAddPortButton", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON}, + {"crostiniPortForwardingAddPortButtonDescription", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON_DESCRIPTION}, + {"crostiniPortForwardingAddPortDialogTitle", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_TITLE}, + {"crostiniPortForwardingAddPortDialogLabel", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL}, + {"crostiniPortForwardingTCP", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP}, + {"crostiniPortForwardingUDP", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP}, + {"crostiniPortForwardingAddError", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_ERROR}, + {"crostiniDiskResizeTitle", IDS_SETTINGS_CROSTINI_DISK_RESIZE_TITLE}, + {"crostiniDiskResizeShowButton", + IDS_SETTINGS_CROSTINI_DISK_RESIZE_SHOW_BUTTON}, + {"crostiniDiskResizeLabel", IDS_SETTINGS_CROSTINI_DISK_RESIZE_LABEL}, + {"crostiniDiskResizeUnsupported", + IDS_SETTINGS_CROSTINI_DISK_RESIZE_UNSUPPORTED}, + {"crostiniDiskResizeLoading", IDS_SETTINGS_CROSTINI_DISK_RESIZE_LOADING}, + {"crostiniDiskResizeError", IDS_SETTINGS_CROSTINI_DISK_RESIZE_ERROR}, + {"crostiniDiskResizeErrorRetry", + IDS_SETTINGS_CROSTINI_DISK_RESIZE_ERROR_RETRY}, + {"crostiniDiskResizeCancel", IDS_SETTINGS_CROSTINI_DISK_RESIZE_CANCEL}, + {"crostiniDiskResizeGoButton", + IDS_SETTINGS_CROSTINI_DISK_RESIZE_GO_BUTTON}, + {"crostiniDiskResizeInProgress", + IDS_SETTINGS_CROSTINI_DISK_RESIZE_IN_PROGRESS}, + {"crostiniDiskResizeResizingError", + IDS_SETTINGS_CROSTINI_DISK_RESIZE_RESIZING_ERROR}, + {"crostiniDiskResizeDone", IDS_SETTINGS_CROSTINI_DISK_RESIZE_DONE}, + {"crostiniMicTitle", IDS_SETTINGS_CROSTINI_MIC_TITLE}, + {"crostiniMicDialogTitle", IDS_SETTINGS_CROSTINI_MIC_DIALOG_TITLE}, + {"crostiniMicDialogLabel", IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + html_source->AddString( + "crostiniSubtext", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_CROSTINI_SUBTEXT, ui::GetChromeOSDeviceName(), + GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL))); + html_source->AddString( + "crostiniArcAdbPowerwashRequiredSublabel", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_CROSTINI_ARC_ADB_POWERWASH_REQUIRED_SUBLABEL, + base::ASCIIToUTF16(chrome::kArcAdbSideloadingLearnMoreURL))); + html_source->AddString("crostiniRemove", l10n_util::GetStringFUTF16( + IDS_SETTINGS_CROSTINI_REMOVE, + ui::GetChromeOSDeviceName())); + html_source->AddString( + "crostiniArcAdbConfirmationMessageEnable", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_MESSAGE_ENABLE, + ui::GetChromeOSDeviceName())); + html_source->AddString( + "crostiniArcAdbConfirmationMessageDisable", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_CROSTINI_ARC_ADB_CONFIRMATION_MESSAGE_DISABLE, + ui::GetChromeOSDeviceName())); + html_source->AddString( + "crostiniSharedPathsInstructionsLocate", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE, + base::ASCIIToUTF16( + crostini::ContainerChromeOSBaseDirectory().value()))); + html_source->AddBoolean( + "showCrostiniExportImport", + crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile)); + html_source->AddBoolean("arcAdbSideloadingSupported", + base::FeatureList::IsEnabled( + chromeos::features::kArcAdbSideloadingFeature)); + html_source->AddBoolean("showCrostiniPortForwarding", + base::FeatureList::IsEnabled( + chromeos::features::kCrostiniPortForwarding)); + html_source->AddBoolean("isOwnerProfile", + chromeos::ProfileHelper::IsOwnerProfile(profile)); + html_source->AddBoolean("isEnterpriseManaged", + IsDeviceManaged() || IsProfileManaged(profile)); + html_source->AddBoolean( + "canChangeAdbSideloading", + crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(profile)); + html_source->AddBoolean("showCrostiniContainerUpgrade", + crostini::ShouldAllowContainerUpgrade(profile)); + html_source->AddBoolean( + "showCrostiniDiskResize", + base::FeatureList::IsEnabled(chromeos::features::kCrostiniDiskResizing)); + html_source->AddBoolean("showCrostiniMic", + base::FeatureList::IsEnabled( + chromeos::features::kCrostiniShowMicSetting)); +} + +void AddPluginVmStrings(content::WebUIDataSource* html_source, + Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"pluginVmPageTitle", IDS_SETTINGS_PLUGIN_VM_PAGE_TITLE}, + {"pluginVmPageLabel", IDS_SETTINGS_PLUGIN_VM_PAGE_LABEL}, + {"pluginVmPageSubtext", IDS_SETTINGS_PLUGIN_VM_PAGE_SUBTEXT}, + {"pluginVmPageEnable", IDS_SETTINGS_TURN_ON}, + {"pluginVmPrinterAccess", IDS_SETTINGS_PLUGIN_VM_PRINTER_ACCESS}, + {"pluginVmSharedPaths", IDS_SETTINGS_PLUGIN_VM_SHARED_PATHS}, + {"pluginVmSharedPathsListHeading", + IDS_SETTINGS_PLUGIN_VM_SHARED_PATHS_LIST_HEADING}, + {"pluginVmSharedPathsInstructionsAdd", + IDS_SETTINGS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_ADD}, + {"pluginVmSharedPathsInstructionsRemove", + IDS_SETTINGS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE}, + {"pluginVmSharedPathsRemoveSharing", + IDS_SETTINGS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING}, + {"pluginVmRemove", IDS_SETTINGS_PLUGIN_VM_REMOVE_LABEL}, + {"pluginVmRemoveButton", IDS_SETTINGS_PLUGIN_VM_REMOVE_BUTTON}, + {"pluginVmRemoveConfirmationDialogMessage", + IDS_SETTINGS_PLUGIN_VM_CONFIRM_REMOVE_DIALOG_BODY}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddAndroidAppStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"androidAppsPageLabel", IDS_SETTINGS_ANDROID_APPS_LABEL}, + {"androidAppsEnable", IDS_SETTINGS_TURN_ON}, + {"androidAppsManageApps", IDS_SETTINGS_ANDROID_APPS_MANAGE_APPS}, + {"androidAppsRemove", IDS_SETTINGS_ANDROID_APPS_REMOVE}, + {"androidAppsRemoveButton", IDS_SETTINGS_ANDROID_APPS_REMOVE_BUTTON}, + {"androidAppsDisableDialogTitle", + IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_TITLE}, + {"androidAppsDisableDialogMessage", + IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_MESSAGE}, + {"androidAppsDisableDialogRemove", + IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_REMOVE}, + {"androidAppsManageAppLinks", IDS_SETTINGS_ANDROID_APPS_MANAGE_APP_LINKS}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + html_source->AddLocalizedString("androidAppsPageTitle", + arc::IsPlayStoreAvailable() + ? IDS_SETTINGS_ANDROID_APPS_TITLE + : IDS_SETTINGS_ANDROID_SETTINGS_TITLE); + html_source->AddString( + "androidAppsSubtext", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_ANDROID_APPS_SUBTEXT, ui::GetChromeOSDeviceName(), + GetHelpUrlWithBoard(chrome::kAndroidAppsLearnMoreURL))); +} + +void AddAppsStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"appsPageTitle", IDS_SETTINGS_APPS_TITLE}, + {"appManagementTitle", IDS_SETTINGS_APPS_LINK_TEXT}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddAppManagementStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"appManagementAppInstalledByPolicyLabel", + IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING}, + {"appManagementCameraPermissionLabel", IDS_APP_MANAGEMENT_CAMERA}, + {"appManagementContactsPermissionLabel", IDS_APP_MANAGEMENT_CONTACTS}, + {"appManagementLocationPermissionLabel", IDS_APP_MANAGEMENT_LOCATION}, + {"appManagementMicrophonePermissionLabel", IDS_APP_MANAGEMENT_MICROPHONE}, + {"appManagementMoreSettingsLabel", IDS_APP_MANAGEMENT_MORE_SETTINGS}, + {"appManagementNoAppsFound", IDS_APP_MANAGEMENT_NO_APPS_FOUND}, + {"appManagementNoPermissions", + IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT}, + {"appManagementNotificationsLabel", IDS_APP_MANAGEMENT_NOTIFICATIONS}, + {"appManagementPermissionsLabel", IDS_APP_MANAGEMENT_PERMISSIONS}, + {"appManagementPinToShelfLabel", IDS_APP_MANAGEMENT_PIN_TO_SHELF}, + {"appManagementSearchPrompt", IDS_APP_MANAGEMENT_SEARCH_PROMPT}, + {"appManagementStoragePermissionLabel", IDS_APP_MANAGEMENT_STORAGE}, + {"appManagementUninstallLabel", IDS_APP_MANAGEMENT_UNINSTALL_APP}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddParentalControlStrings(content::WebUIDataSource* html_source, + Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"parentalControlsPageTitle", IDS_SETTINGS_PARENTAL_CONTROLS_PAGE_TITLE}, + {"parentalControlsPageSetUpLabel", + IDS_SETTINGS_PARENTAL_CONTROLS_PAGE_SET_UP_LABEL}, + {"parentalControlsPageViewSettingsLabel", + IDS_SETTINGS_PARENTAL_CONTROLS_PAGE_VIEW_SETTINGS_LABEL}, + {"parentalControlsPageConnectToInternetLabel", + IDS_SETTINGS_PARENTAL_CONTROLS_PAGE_CONNECT_TO_INTERNET_LABEL}, + {"parentalControlsSetUpButtonLabel", + IDS_SETTINGS_PARENTAL_CONTROLS_SET_UP_BUTTON_LABEL}, + {"parentalControlsSetUpButtonRole", + IDS_SETTINGS_PARENTAL_CONTROLS_SET_UP_BUTTON_ROLE}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddBoolean( + "isChild", user_manager::UserManager::Get()->IsLoggedInAsChildUser()); + + if (user_manager::UserManager::Get()->IsLoggedInAsChildUser()) { + SupervisedUserService* supervised_user_service = + SupervisedUserServiceFactory::GetForProfile(profile); + std::string custodian = supervised_user_service->GetCustodianName(); + std::string second_custodian = + supervised_user_service->GetSecondCustodianName(); + + base::string16 child_managed_tooltip; + if (second_custodian.empty()) { + child_managed_tooltip = l10n_util::GetStringFUTF16( + IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_ONE_PARENT_TOOLTIP, + base::UTF8ToUTF16(custodian)); + } else { + child_managed_tooltip = l10n_util::GetStringFUTF16( + IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP, + base::UTF8ToUTF16(custodian), base::UTF8ToUTF16(second_custodian)); + } + html_source->AddString("accountManagerPrimaryAccountChildManagedTooltip", + child_managed_tooltip); + } +} + +void AddBluetoothStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"bluetoothConnected", IDS_SETTINGS_BLUETOOTH_CONNECTED}, + {"bluetoothConnectedWithBattery", + IDS_SETTINGS_BLUETOOTH_CONNECTED_WITH_BATTERY}, + {"bluetoothConnecting", IDS_SETTINGS_BLUETOOTH_CONNECTING}, + {"bluetoothDeviceListPaired", IDS_SETTINGS_BLUETOOTH_DEVICE_LIST_PAIRED}, + {"bluetoothDeviceListUnpaired", + IDS_SETTINGS_BLUETOOTH_DEVICE_LIST_UNPAIRED}, + {"bluetoothConnect", IDS_SETTINGS_BLUETOOTH_CONNECT}, + {"bluetoothDisconnect", IDS_SETTINGS_BLUETOOTH_DISCONNECT}, + {"bluetoothToggleA11yLabel", + IDS_SETTINGS_BLUETOOTH_TOGGLE_ACCESSIBILITY_LABEL}, + {"bluetoothExpandA11yLabel", + IDS_SETTINGS_BLUETOOTH_EXPAND_ACCESSIBILITY_LABEL}, + {"bluetoothNoDevices", IDS_SETTINGS_BLUETOOTH_NO_DEVICES}, + {"bluetoothNoDevicesFound", IDS_SETTINGS_BLUETOOTH_NO_DEVICES_FOUND}, + {"bluetoothNotConnected", IDS_SETTINGS_BLUETOOTH_NOT_CONNECTED}, + {"bluetoothPageTitle", IDS_SETTINGS_BLUETOOTH}, + {"bluetoothPairDevicePageTitle", + IDS_SETTINGS_BLUETOOTH_PAIR_DEVICE_TITLE}, + {"bluetoothRemove", IDS_SETTINGS_BLUETOOTH_REMOVE}, + {"bluetoothPrimaryUserControlled", + IDS_SETTINGS_BLUETOOTH_PRIMARY_USER_CONTROLLED}, + {"bluetoothDeviceType_computer", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_COMPUTER}, + {"bluetoothDeviceType_phone", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_PHONE}, + {"bluetoothDeviceType_modem", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_MODEM}, + {"bluetoothDeviceType_audio", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_AUDIO}, + {"bluetoothDeviceType_carAudio", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_CAR_AUDIO}, + {"bluetoothDeviceType_video", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_VIDEO}, + {"bluetoothDeviceType_peripheral", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_PERIPHERAL}, + {"bluetoothDeviceType_joystick", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_JOYSTICK}, + {"bluetoothDeviceType_gamepad", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_GAMEPAD}, + {"bluetoothDeviceType_keyboard", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_KEYBOARD}, + {"bluetoothDeviceType_mouse", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_MOUSE}, + {"bluetoothDeviceType_tablet", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_TABLET}, + {"bluetoothDeviceType_keyboardMouseCombo", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_KEYBOARD_MOUSE_COMBO}, + {"bluetoothDeviceType_unknown", + IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_UNKNOWN}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + chromeos::bluetooth_dialog::AddLocalizedStrings(html_source); +} + +void AddChromeOSUserStrings(content::WebUIDataSource* html_source, + Profile* profile) { + user_manager::UserManager* user_manager = user_manager::UserManager::Get(); + + const user_manager::User* user = + chromeos::ProfileHelper::Get()->GetUserByProfile(profile); + const user_manager::User* primary_user = user_manager->GetPrimaryUser(); + std::string primary_user_email = primary_user->GetAccountId().GetUserEmail(); + html_source->AddString("primaryUserEmail", primary_user_email); + html_source->AddString("browserSettingsBannerText", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_BROWSER_SETTINGS_BANNER, + base::ASCIIToUTF16(chrome::kChromeUISettingsURL))); + html_source->AddBoolean("isActiveDirectoryUser", + user && user->IsActiveDirectoryUser()); + html_source->AddBoolean( + "isSecondaryUser", + user && user->GetAccountId() != primary_user->GetAccountId()); + html_source->AddString( + "secondaryUserBannerText", + l10n_util::GetStringFUTF16(IDS_SETTINGS_SECONDARY_USER_BANNER, + base::ASCIIToUTF16(primary_user_email))); + + if (!IsDeviceManaged() && !user_manager->IsCurrentUserOwner()) { + html_source->AddString("ownerEmail", + user_manager->GetOwnerAccountId().GetUserEmail()); + } +} + +void AddDevicePointersStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kPointersStrings[] = { + {"mouseTitle", IDS_SETTINGS_MOUSE_TITLE}, + {"touchpadTitle", IDS_SETTINGS_TOUCHPAD_TITLE}, + {"mouseAndTouchpadTitle", IDS_SETTINGS_MOUSE_AND_TOUCHPAD_TITLE}, + {"touchpadTapToClickEnabledLabel", + IDS_SETTINGS_TOUCHPAD_TAP_TO_CLICK_ENABLED_LABEL}, + {"touchpadSpeed", IDS_SETTINGS_TOUCHPAD_SPEED_LABEL}, + {"pointerSlow", IDS_SETTINGS_POINTER_SPEED_SLOW_LABEL}, + {"pointerFast", IDS_SETTINGS_POINTER_SPEED_FAST_LABEL}, + {"mouseScrollSpeed", IDS_SETTINGS_MOUSE_SCROLL_SPEED_LABEL}, + {"mouseSpeed", IDS_SETTINGS_MOUSE_SPEED_LABEL}, + {"mouseSwapButtons", IDS_SETTINGS_MOUSE_SWAP_BUTTONS_LABEL}, + {"mouseReverseScroll", IDS_SETTINGS_MOUSE_REVERSE_SCROLL_LABEL}, + {"mouseAccelerationLabel", IDS_SETTINGS_MOUSE_ACCELERATION_LABEL}, + {"mouseScrollAccelerationLabel", + IDS_SETTINGS_MOUSE_SCROLL_ACCELERATION_LABEL}, + {"touchpadAccelerationLabel", IDS_SETTINGS_TOUCHPAD_ACCELERATION_LABEL}, + {"touchpadScrollAccelerationLabel", + IDS_SETTINGS_TOUCHPAD_SCROLL_ACCELERATION_LABEL}, + {"touchpadScrollSpeed", IDS_SETTINGS_TOUCHPAD_SCROLL_SPEED_LABEL}, + }; + AddLocalizedStringsBulk(html_source, kPointersStrings); + + html_source->AddString("naturalScrollLearnMoreLink", + GetHelpUrlWithBoard(chrome::kNaturalScrollHelpURL)); + + html_source->AddBoolean( + "allowDisableMouseAcceleration", + base::FeatureList::IsEnabled(::features::kAllowDisableMouseAcceleration)); + html_source->AddBoolean( + "allowScrollSettings", + base::FeatureList::IsEnabled(features::kAllowScrollSettings)); +} + +void AddDeviceKeyboardStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString keyboard_strings[] = { + {"keyboardTitle", IDS_SETTINGS_KEYBOARD_TITLE}, + {"keyboardKeyCtrl", IDS_SETTINGS_KEYBOARD_KEY_LEFT_CTRL}, + {"keyboardKeyAlt", IDS_SETTINGS_KEYBOARD_KEY_LEFT_ALT}, + {"keyboardKeyCapsLock", IDS_SETTINGS_KEYBOARD_KEY_CAPS_LOCK}, + {"keyboardKeyCommand", IDS_SETTINGS_KEYBOARD_KEY_COMMAND}, + {"keyboardKeyDiamond", IDS_SETTINGS_KEYBOARD_KEY_DIAMOND}, + {"keyboardKeyEscape", IDS_SETTINGS_KEYBOARD_KEY_ESCAPE}, + {"keyboardKeyBackspace", IDS_SETTINGS_KEYBOARD_KEY_BACKSPACE}, + {"keyboardKeyAssistant", IDS_SETTINGS_KEYBOARD_KEY_ASSISTANT}, + {"keyboardKeyDisabled", IDS_SETTINGS_KEYBOARD_KEY_DISABLED}, + {"keyboardKeyExternalCommand", + IDS_SETTINGS_KEYBOARD_KEY_EXTERNAL_COMMAND}, + {"keyboardKeyExternalMeta", IDS_SETTINGS_KEYBOARD_KEY_EXTERNAL_META}, + {"keyboardKeyMeta", IDS_SETTINGS_KEYBOARD_KEY_META}, + {"keyboardSendFunctionKeys", IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS}, + {"keyboardEnableAutoRepeat", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_ENABLE}, + {"keyRepeatDelay", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY}, + {"keyRepeatDelayLong", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_LONG}, + {"keyRepeatDelayShort", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_DELAY_SHORT}, + {"keyRepeatRate", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE}, + {"keyRepeatRateSlow", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_RATE_SLOW}, + {"keyRepeatRateFast", IDS_SETTINGS_KEYBOARD_AUTO_REPEAT_FAST}, + {"showKeyboardShortcutViewer", + IDS_SETTINGS_KEYBOARD_SHOW_SHORTCUT_VIEWER}, + {"keyboardShowLanguageAndInput", + IDS_SETTINGS_KEYBOARD_SHOW_LANGUAGE_AND_INPUT}, + }; + AddLocalizedStringsBulk(html_source, keyboard_strings); + + html_source->AddLocalizedString("keyboardKeySearch", + ui::DeviceUsesKeyboardLayout2() + ? IDS_SETTINGS_KEYBOARD_KEY_LAUNCHER + : IDS_SETTINGS_KEYBOARD_KEY_SEARCH); + html_source->AddLocalizedString( + "keyboardSendFunctionKeysDescription", + ui::DeviceUsesKeyboardLayout2() + ? IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_LAYOUT2_DESCRIPTION + : IDS_SETTINGS_KEYBOARD_SEND_FUNCTION_KEYS_DESCRIPTION); +} + +void AddDeviceStylusStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kStylusStrings[] = { + {"stylusTitle", IDS_SETTINGS_STYLUS_TITLE}, + {"stylusEnableStylusTools", IDS_SETTINGS_STYLUS_ENABLE_STYLUS_TOOLS}, + {"stylusAutoOpenStylusTools", IDS_SETTINGS_STYLUS_AUTO_OPEN_STYLUS_TOOLS}, + {"stylusFindMoreAppsPrimary", IDS_SETTINGS_STYLUS_FIND_MORE_APPS_PRIMARY}, + {"stylusFindMoreAppsSecondary", + IDS_SETTINGS_STYLUS_FIND_MORE_APPS_SECONDARY}, + {"stylusNoteTakingApp", IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_LABEL}, + {"stylusNoteTakingAppEnabledOnLockScreen", + IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_LOCK_SCREEN_CHECKBOX}, + {"stylusNoteTakingAppKeepsLastNoteOnLockScreen", + IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_KEEP_LATEST_NOTE}, + {"stylusNoteTakingAppLockScreenSettingsHeader", + IDS_SETTINGS_STYLUS_LOCK_SCREEN_NOTES_TITLE}, + {"stylusNoteTakingAppNoneAvailable", + IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_NONE_AVAILABLE}, + {"stylusNoteTakingAppWaitingForAndroid", + IDS_SETTINGS_STYLUS_NOTE_TAKING_APP_WAITING_FOR_ANDROID}}; + AddLocalizedStringsBulk(html_source, kStylusStrings); +} + +void AddDeviceDisplayStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kDisplayStrings[] = { + {"displayTitle", IDS_SETTINGS_DISPLAY_TITLE}, + {"displayArrangementText", IDS_SETTINGS_DISPLAY_ARRANGEMENT_TEXT}, + {"displayArrangementTitle", IDS_SETTINGS_DISPLAY_ARRANGEMENT_TITLE}, + {"displayMirror", IDS_SETTINGS_DISPLAY_MIRROR}, + {"displayMirrorDisplayName", IDS_SETTINGS_DISPLAY_MIRROR_DISPLAY_NAME}, + {"displayAmbientColorTitle", IDS_SETTINGS_DISPLAY_AMBIENT_COLOR_TITLE}, + {"displayAmbientColorSubtitle", + IDS_SETTINGS_DISPLAY_AMBIENT_COLOR_SUBTITLE}, + {"displayNightLightLabel", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_LABEL}, + {"displayNightLightOnAtSunset", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_ON_AT_SUNSET}, + {"displayNightLightOffAtSunrise", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_OFF_AT_SUNRISE}, + {"displayNightLightScheduleCustom", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_CUSTOM}, + {"displayNightLightScheduleLabel", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_LABEL}, + {"displayNightLightScheduleNever", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_NEVER}, + {"displayNightLightScheduleSunsetToSunRise", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_SCHEDULE_SUNSET_TO_SUNRISE}, + {"displayNightLightStartTime", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_START_TIME}, + {"displayNightLightStopTime", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_STOP_TIME}, + {"displayNightLightText", IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEXT}, + {"displayNightLightTemperatureLabel", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMPERATURE_LABEL}, + {"displayNightLightTempSliderMaxLabel", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMP_SLIDER_MAX_LABEL}, + {"displayNightLightTempSliderMinLabel", + IDS_SETTINGS_DISPLAY_NIGHT_LIGHT_TEMP_SLIDER_MIN_LABEL}, + {"displayUnifiedDesktop", IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP}, + {"displayUnifiedDesktopOn", IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_ON}, + {"displayUnifiedDesktopOff", IDS_SETTINGS_DISPLAY_UNIFIED_DESKTOP_OFF}, + {"displayResolutionTitle", IDS_SETTINGS_DISPLAY_RESOLUTION_TITLE}, + {"displayResolutionText", IDS_SETTINGS_DISPLAY_RESOLUTION_TEXT}, + {"displayResolutionTextBest", IDS_SETTINGS_DISPLAY_RESOLUTION_TEXT_BEST}, + {"displayResolutionTextNative", + IDS_SETTINGS_DISPLAY_RESOLUTION_TEXT_NATIVE}, + {"displayResolutionSublabel", IDS_SETTINGS_DISPLAY_RESOLUTION_SUBLABEL}, + {"displayResolutionMenuItem", IDS_SETTINGS_DISPLAY_RESOLUTION_MENU_ITEM}, + {"displayResolutionInterlacedMenuItem", + IDS_SETTINGS_DISPLAY_RESOLUTION_INTERLACED_MENU_ITEM}, + {"displayZoomTitle", IDS_SETTINGS_DISPLAY_ZOOM_TITLE}, + {"displayZoomSublabel", IDS_SETTINGS_DISPLAY_ZOOM_SUBLABEL}, + {"displayZoomValue", IDS_SETTINGS_DISPLAY_ZOOM_VALUE}, + {"displayZoomLogicalResolutionText", + IDS_SETTINGS_DISPLAY_ZOOM_LOGICAL_RESOLUTION_TEXT}, + {"displayZoomNativeLogicalResolutionNativeText", + IDS_SETTINGS_DISPLAY_ZOOM_LOGICAL_RESOLUTION_NATIVE_TEXT}, + {"displayZoomLogicalResolutionDefaultText", + IDS_SETTINGS_DISPLAY_ZOOM_LOGICAL_RESOLUTION_DEFAULT_TEXT}, + {"displaySizeSliderMinLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MINIMUM}, + {"displaySizeSliderMaxLabel", IDS_SETTINGS_DISPLAY_ZOOM_SLIDER_MAXIMUM}, + {"displayScreenTitle", IDS_SETTINGS_DISPLAY_SCREEN}, + {"displayScreenExtended", IDS_SETTINGS_DISPLAY_SCREEN_EXTENDED}, + {"displayScreenPrimary", IDS_SETTINGS_DISPLAY_SCREEN_PRIMARY}, + {"displayOrientation", IDS_SETTINGS_DISPLAY_ORIENTATION}, + {"displayOrientationStandard", IDS_SETTINGS_DISPLAY_ORIENTATION_STANDARD}, + {"displayOrientationAutoRotate", + IDS_SETTINGS_DISPLAY_ORIENTATION_AUTO_ROTATE}, + {"displayOverscanPageText", IDS_SETTINGS_DISPLAY_OVERSCAN_TEXT}, + {"displayOverscanPageTitle", IDS_SETTINGS_DISPLAY_OVERSCAN_TITLE}, + {"displayOverscanSubtitle", IDS_SETTINGS_DISPLAY_OVERSCAN_SUBTITLE}, + {"displayOverscanInstructions", + IDS_SETTINGS_DISPLAY_OVERSCAN_INSTRUCTIONS}, + {"displayOverscanResize", IDS_SETTINGS_DISPLAY_OVERSCAN_RESIZE}, + {"displayOverscanPosition", IDS_SETTINGS_DISPLAY_OVERSCAN_POSITION}, + {"displayOverscanReset", IDS_SETTINGS_DISPLAY_OVERSCAN_RESET}, + {"displayTouchCalibrationTitle", + IDS_SETTINGS_DISPLAY_TOUCH_CALIBRATION_TITLE}, + {"displayTouchCalibrationText", + IDS_SETTINGS_DISPLAY_TOUCH_CALIBRATION_TEXT}}; + AddLocalizedStringsBulk(html_source, kDisplayStrings); + + base::CommandLine& cmd = *base::CommandLine::ForCurrentProcess(); + html_source->AddBoolean("unifiedDesktopAvailable", + cmd.HasSwitch(::switches::kEnableUnifiedDesktop)); + + html_source->AddBoolean("listAllDisplayModes", + display::features::IsListAllDisplayModesEnabled()); + + html_source->AddBoolean("deviceSupportsAmbientColor", + ash::features::IsAllowAmbientEQEnabled()); + + html_source->AddBoolean( + "enableTouchCalibrationSetting", + cmd.HasSwitch(chromeos::switches::kEnableTouchCalibrationSetting)); + + html_source->AddBoolean("hasExternalTouchDevice", + display::HasExternalTouchscreenDevice()); +} + +void AddDeviceStorageStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kStorageStrings[] = { + {"storageTitle", IDS_SETTINGS_STORAGE_TITLE}, + {"storageItemInUse", IDS_SETTINGS_STORAGE_ITEM_IN_USE}, + {"storageItemAvailable", IDS_SETTINGS_STORAGE_ITEM_AVAILABLE}, + {"storageItemSystem", IDS_SETTINGS_STORAGE_ITEM_SYSTEM}, + {"storageItemMyFiles", IDS_SETTINGS_STORAGE_ITEM_MY_FILES}, + {"storageItemBrowsingData", IDS_SETTINGS_STORAGE_ITEM_BROWSING_DATA}, + {"storageItemApps", IDS_SETTINGS_STORAGE_ITEM_APPS}, + {"storageItemCrostini", IDS_SETTINGS_STORAGE_ITEM_CROSTINI}, + {"storageItemOtherUsers", IDS_SETTINGS_STORAGE_ITEM_OTHER_USERS}, + {"storageSizeComputing", IDS_SETTINGS_STORAGE_SIZE_CALCULATING}, + {"storageSizeUnknown", IDS_SETTINGS_STORAGE_SIZE_UNKNOWN}, + {"storageSpaceLowMessageTitle", + IDS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_TITLE}, + {"storageSpaceLowMessageLine1", + IDS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_LINE_1}, + {"storageSpaceLowMessageLine2", + IDS_SETTINGS_STORAGE_SPACE_LOW_MESSAGE_LINE_2}, + {"storageSpaceCriticallyLowMessageTitle", + IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_TITLE}, + {"storageSpaceCriticallyLowMessageLine1", + IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_1}, + {"storageSpaceCriticallyLowMessageLine2", + IDS_SETTINGS_STORAGE_SPACE_CRITICALLY_LOW_MESSAGE_LINE_2}, + {"storageExternal", IDS_SETTINGS_STORAGE_EXTERNAL}, + {"storageExternalStorageEmptyListHeader", + IDS_SETTINGS_STORAGE_EXTERNAL_STORAGE_EMPTY_LIST_HEADER}, + {"storageExternalStorageListHeader", + IDS_SETTINGS_STORAGE_EXTERNAL_STORAGE_LIST_HEADER}, + {"storageOverviewAriaLabel", IDS_SETTINGS_STORAGE_OVERVIEW_ARIA_LABEL}}; + AddLocalizedStringsBulk(html_source, kStorageStrings); + + html_source->AddString( + "storageAndroidAppsExternalDrivesNote", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_STORAGE_ANDROID_APPS_ACCESS_EXTERNAL_DRIVES_NOTE, + base::ASCIIToUTF16(chrome::kArcExternalStorageLearnMoreURL))); +} + +void AddDevicePowerStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kPowerStrings[] = { + {"powerTitle", IDS_SETTINGS_POWER_TITLE}, + {"powerSourceLabel", IDS_SETTINGS_POWER_SOURCE_LABEL}, + {"powerSourceBattery", IDS_SETTINGS_POWER_SOURCE_BATTERY}, + {"powerSourceAcAdapter", IDS_SETTINGS_POWER_SOURCE_AC_ADAPTER}, + {"powerSourceLowPowerCharger", + IDS_SETTINGS_POWER_SOURCE_LOW_POWER_CHARGER}, + {"calculatingPower", IDS_SETTINGS_POWER_SOURCE_CALCULATING}, + {"powerIdleLabel", IDS_SETTINGS_POWER_IDLE_LABEL}, + {"powerIdleWhileChargingLabel", + IDS_SETTINGS_POWER_IDLE_WHILE_CHARGING_LABEL}, + {"powerIdleWhileChargingAriaLabel", + IDS_SETTINGS_POWER_IDLE_WHILE_CHARGING_ARIA_LABEL}, + {"powerIdleWhileOnBatteryLabel", + IDS_SETTINGS_POWER_IDLE_WHILE_ON_BATTERY_LABEL}, + {"powerIdleWhileOnBatteryAriaLabel", + IDS_SETTINGS_POWER_IDLE_WHILE_ON_BATTERY_ARIA_LABEL}, + {"powerIdleDisplayOffSleep", IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF_SLEEP}, + {"powerIdleDisplayOff", IDS_SETTINGS_POWER_IDLE_DISPLAY_OFF}, + {"powerIdleDisplayOn", IDS_SETTINGS_POWER_IDLE_DISPLAY_ON}, + {"powerIdleOther", IDS_SETTINGS_POWER_IDLE_OTHER}, + {"powerLidSleepLabel", IDS_SETTINGS_POWER_LID_CLOSED_SLEEP_LABEL}, + {"powerLidSignOutLabel", IDS_SETTINGS_POWER_LID_CLOSED_SIGN_OUT_LABEL}, + {"powerLidShutDownLabel", IDS_SETTINGS_POWER_LID_CLOSED_SHUT_DOWN_LABEL}, + }; + AddLocalizedStringsBulk(html_source, kPowerStrings); +} + +void AddDeviceStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kDeviceStrings[] = { + {"devicePageTitle", IDS_SETTINGS_DEVICE_TITLE}, + {"scrollLabel", IDS_SETTINGS_SCROLL_LABEL}, + {"touchPadScrollLabel", IDS_OS_SETTINGS_TOUCHPAD_REVERSE_SCROLL_LABEL}, + }; + AddLocalizedStringsBulk(html_source, kDeviceStrings); + + AddDevicePointersStrings(html_source); + AddDeviceKeyboardStrings(html_source); + AddDeviceStylusStrings(html_source); + AddDeviceDisplayStrings(html_source); + AddDeviceStorageStrings(html_source); + AddDevicePowerStrings(html_source); +} + +void AddFilesStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"disconnectGoogleDriveAccount", IDS_SETTINGS_DISCONNECT_GOOGLE_DRIVE}, + {"filesPageTitle", IDS_OS_SETTINGS_FILES}, + {"smbSharesTitle", IDS_SETTINGS_DOWNLOADS_SMB_SHARES}, + {"smbSharesLearnMoreLabel", + IDS_SETTINGS_DOWNLOADS_SMB_SHARES_LEARN_MORE_LABEL}, + {"addSmbShare", IDS_SETTINGS_DOWNLOADS_SMB_SHARES_ADD_SHARE}, + {"smbShareAddedSuccessfulMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_SUCCESS_MESSAGE}, + {"smbShareAddedErrorMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_ERROR_MESSAGE}, + {"smbShareAddedAuthFailedMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_AUTH_FAILED_MESSAGE}, + {"smbShareAddedNotFoundMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_NOT_FOUND_MESSAGE}, + {"smbShareAddedUnsupportedDeviceMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_UNSUPPORTED_DEVICE_MESSAGE}, + {"smbShareAddedMountExistsMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_MOUNT_EXISTS_MESSAGE}, + {"smbShareAddedTooManyMountsMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_TOO_MANY_MOUNTS_MESSAGE}, + {"smbShareAddedInvalidURLMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_MOUNT_INVALID_URL_MESSAGE}, + {"smbShareAddedInvalidSSOURLMessage", + IDS_SETTINGS_DOWNLOADS_SHARE_ADDED_MOUNT_INVALID_SSO_URL_MESSAGE}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + chromeos::smb_dialog::AddLocalizedStrings(html_source); + + html_source->AddString("smbSharesLearnMoreURL", + GetHelpUrlWithBoard(chrome::kSmbSharesLearnMoreURL)); +} + +void AddEasyUnlockStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"easyUnlockSectionTitle", IDS_SETTINGS_EASY_UNLOCK_SECTION_TITLE}, + {"easyUnlockUnlockDeviceOnly", + IDS_SETTINGS_EASY_UNLOCK_UNLOCK_DEVICE_ONLY}, + {"easyUnlockUnlockDeviceAndAllowSignin", + IDS_SETTINGS_EASY_UNLOCK_UNLOCK_DEVICE_AND_ALLOW_SIGNIN}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddMultideviceStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"multidevicePageTitle", IDS_SETTINGS_MULTIDEVICE}, + {"multideviceSetupButton", IDS_SETTINGS_MULTIDEVICE_SETUP_BUTTON}, + {"multideviceVerifyButton", IDS_SETTINGS_MULTIDEVICE_VERIFY_BUTTON}, + {"multideviceSetupItemHeading", + IDS_SETTINGS_MULTIDEVICE_SETUP_ITEM_HEADING}, + {"multideviceEnabled", IDS_SETTINGS_MULTIDEVICE_ENABLED}, + {"multideviceDisabled", IDS_SETTINGS_MULTIDEVICE_DISABLED}, + {"multideviceSmartLockItemTitle", IDS_SETTINGS_EASY_UNLOCK_SECTION_TITLE}, + {"multideviceInstantTetheringItemTitle", + IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING}, + {"multideviceInstantTetheringItemSummary", + IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING_SUMMARY}, + {"multideviceAndroidMessagesItemTitle", + IDS_SETTINGS_MULTIDEVICE_ANDROID_MESSAGES}, + {"multideviceForgetDevice", IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE}, + {"multideviceSmartLockOptions", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS_LOCK}, + {"multideviceForgetDeviceDisconnect", + IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE_DISCONNECT}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString( + "multideviceForgetDeviceSummary", + ui::SubstituteChromeOSDeviceType( + IDS_SETTINGS_MULTIDEVICE_FORGET_THIS_DEVICE_EXPLANATION)); + html_source->AddString( + "multideviceForgetDeviceDialogMessage", + ui::SubstituteChromeOSDeviceType( + IDS_SETTINGS_MULTIDEVICE_FORGET_DEVICE_DIALOG_MESSAGE)); + html_source->AddString( + "multideviceVerificationText", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_MULTIDEVICE_VERIFICATION_TEXT, + base::UTF8ToUTF16( + chromeos::multidevice_setup:: + GetBoardSpecificBetterTogetherSuiteLearnMoreUrl() + .spec()))); + html_source->AddString( + "multideviceSetupSummary", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_MULTIDEVICE_SETUP_SUMMARY, ui::GetChromeOSDeviceName(), + base::UTF8ToUTF16( + chromeos::multidevice_setup:: + GetBoardSpecificBetterTogetherSuiteLearnMoreUrl() + .spec()))); + html_source->AddString( + "multideviceNoHostText", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_MULTIDEVICE_NO_ELIGIBLE_HOSTS, + base::UTF8ToUTF16( + chromeos::multidevice_setup:: + GetBoardSpecificBetterTogetherSuiteLearnMoreUrl() + .spec()))); + html_source->AddString( + "multideviceAndroidMessagesItemSummary", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_MULTIDEVICE_ANDROID_MESSAGES_SUMMARY, + ui::GetChromeOSDeviceName(), + base::UTF8ToUTF16(chromeos::multidevice_setup:: + GetBoardSpecificMessagesLearnMoreUrl() + .spec()))); + html_source->AddString( + "multideviceSmartLockItemSummary", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_MULTIDEVICE_SMART_LOCK_SUMMARY, + ui::GetChromeOSDeviceName(), + GetHelpUrlWithBoard(chrome::kEasyUnlockLearnMoreUrl))); + + AddEasyUnlockStrings(html_source); +} + +void AddKerberosAccountsPageStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"kerberosAccountsAddAccountLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_ADD_ACCOUNT_LABEL}, + {"kerberosAccountsRefreshNowLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_REFRESH_NOW_LABEL}, + {"kerberosAccountsSetAsActiveAccountLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_SET_AS_ACTIVE_ACCOUNT_LABEL}, + {"kerberosAccountsSignedOut", IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_OUT}, + {"kerberosAccountsListHeader", + IDS_SETTINGS_KERBEROS_ACCOUNTS_LIST_HEADER}, + {"kerberosAccountsRemoveAccountLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_REMOVE_ACCOUNT_LABEL}, + {"kerberosAccountsReauthenticationLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_REAUTHENTICATION_LABEL}, + {"kerberosAccountsTicketActive", + IDS_SETTINGS_KERBEROS_ACCOUNTS_TICKET_ACTIVE}, + {"kerberosAccountsAccountRemovedTip", + IDS_SETTINGS_KERBEROS_ACCOUNTS_ACCOUNT_REMOVED_TIP}, + {"kerberosAccountsAccountRefreshedTip", + IDS_SETTINGS_KERBEROS_ACCOUNTS_ACCOUNT_REFRESHED_TIP}, + {"kerberosAccountsSignedIn", IDS_SETTINGS_KERBEROS_ACCOUNTS_SIGNED_IN}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + PrefService* local_state = g_browser_process->local_state(); + + // Whether new Kerberos accounts may be added. + html_source->AddBoolean( + "kerberosAddAccountsAllowed", + local_state->GetBoolean(prefs::kKerberosAddAccountsAllowed)); + + // Kerberos accounts page with "Learn more" link. + html_source->AddString( + "kerberosAccountsDescription", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_KERBEROS_ACCOUNTS_DESCRIPTION, + GetHelpUrlWithBoard(chrome::kKerberosAccountsLearnMoreURL))); +} + +void AddKerberosAddAccountDialogStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"kerberosAccountsAdvancedConfigLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_ADVANCED_CONFIG_LABEL}, + {"kerberosAdvancedConfigTitle", + IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_TITLE}, + {"kerberosAdvancedConfigDesc", + IDS_SETTINGS_KERBEROS_ADVANCED_CONFIG_DESC}, + {"addKerberosAccountRememberPassword", + IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REMEMBER_PASSWORD}, + {"kerberosPassword", IDS_SETTINGS_KERBEROS_PASSWORD}, + {"kerberosUsername", IDS_SETTINGS_KERBEROS_USERNAME}, + {"addKerberosAccountDescription", + IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_DESCRIPTION}, + {"kerberosErrorNetworkProblem", + IDS_SETTINGS_KERBEROS_ERROR_NETWORK_PROBLEM}, + {"kerberosErrorUsernameInvalid", + IDS_SETTINGS_KERBEROS_ERROR_USERNAME_INVALID}, + {"kerberosErrorUsernameUnknown", + IDS_SETTINGS_KERBEROS_ERROR_USERNAME_UNKNOWN}, + {"kerberosErrorDuplicatePrincipalName", + IDS_SETTINGS_KERBEROS_ERROR_DUPLICATE_PRINCIPAL_NAME}, + {"kerberosErrorContactingServer", + IDS_SETTINGS_KERBEROS_ERROR_CONTACTING_SERVER}, + {"kerberosErrorPasswordInvalid", + IDS_SETTINGS_KERBEROS_ERROR_PASSWORD_INVALID}, + {"kerberosErrorPasswordExpired", + IDS_SETTINGS_KERBEROS_ERROR_PASSWORD_EXPIRED}, + {"kerberosErrorKdcEncType", IDS_SETTINGS_KERBEROS_ERROR_KDC_ENC_TYPE}, + {"kerberosErrorGeneral", IDS_SETTINGS_KERBEROS_ERROR_GENERAL}, + {"kerberosConfigErrorSectionNestedInGroup", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_SECTION_NESTED_IN_GROUP}, + {"kerberosConfigErrorSectionSyntax", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_SECTION_SYNTAX}, + {"kerberosConfigErrorExpectedOpeningCurlyBrace", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_EXPECTED_OPENING_CURLY_BRACE}, + {"kerberosConfigErrorExtraCurlyBrace", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_EXTRA_CURLY_BRACE}, + {"kerberosConfigErrorRelationSyntax", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_RELATION_SYNTAX_ERROR}, + {"kerberosConfigErrorKeyNotSupported", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_KEY_NOT_SUPPORTED}, + {"kerberosConfigErrorSectionNotSupported", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_SECTION_NOT_SUPPORTED}, + {"kerberosConfigErrorKrb5FailedToParse", + IDS_SETTINGS_KERBEROS_CONFIG_ERROR_KRB5_FAILED_TO_PARSE}, + {"addKerberosAccountRefreshButtonLabel", + IDS_SETTINGS_ADD_KERBEROS_ACCOUNT_REFRESH_BUTTON_LABEL}, + {"addKerberosAccount", IDS_SETTINGS_ADD_KERBEROS_ACCOUNT}, + {"refreshKerberosAccount", IDS_SETTINGS_REFRESH_KERBEROS_ACCOUNT}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + PrefService* local_state = g_browser_process->local_state(); + + // Whether the 'Remember password' checkbox is enabled. + html_source->AddBoolean( + "kerberosRememberPasswordEnabled", + local_state->GetBoolean(prefs::kKerberosRememberPasswordEnabled)); + + // Kerberos default configuration. + html_source->AddString( + "defaultKerberosConfig", + chromeos::KerberosCredentialsManager::GetDefaultKerberosConfig()); +} + +void AddLockScreenPageStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"lockScreenNotificationTitle", + IDS_ASH_SETTINGS_LOCK_SCREEN_NOTIFICATION_TITLE}, + {"lockScreenNotificationHideSensitive", + IDS_ASH_SETTINGS_LOCK_SCREEN_NOTIFICATION_HIDE_SENSITIVE}, + {"enableScreenlock", IDS_SETTINGS_PEOPLE_ENABLE_SCREENLOCK}, + {"lockScreenNotificationShow", + IDS_ASH_SETTINGS_LOCK_SCREEN_NOTIFICATION_SHOW}, + {"lockScreenPinOrPassword", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PIN_OR_PASSWORD}, + {"lockScreenSetupFingerprintButton", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SETUP_BUTTON}, + {"lockScreenNotificationHide", + IDS_ASH_SETTINGS_LOCK_SCREEN_NOTIFICATION_HIDE}, + {"lockScreenEditFingerprints", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS}, + {"lockScreenPasswordOnly", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_PASSWORD_ONLY}, + {"lockScreenChangePinButton", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_CHANGE_PIN_BUTTON}, + {"lockScreenEditFingerprintsDescription", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_EDIT_FINGERPRINTS_DESCRIPTION}, + {"lockScreenNumberFingerprints", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NUM_FINGERPRINTS}, + {"lockScreenNone", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NONE}, + {"lockScreenFingerprintNewName", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME}, + {"lockScreenDeleteFingerprintLabel", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_DELETE_FINGERPRINT_ARIA_LABEL}, + {"lockScreenOptionsLock", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS_LOCK}, + {"lockScreenOptionsLoginLock", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_OPTIONS_LOGIN_LOCK}, + {"lockScreenSetupPinButton", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_SETUP_PIN_BUTTON}, + {"lockScreenTitleLock", IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE_LOCK}, + {"lockScreenTitleLoginLock", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE_LOGIN_LOCK}, + {"passwordPromptEnterPasswordLock", + IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_ENTER_PASSWORD_LOCK}, + {"passwordPromptEnterPasswordLoginLock", + IDS_SETTINGS_PEOPLE_PASSWORD_PROMPT_ENTER_PASSWORD_LOGIN_LOCK}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddUsersStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"usersModifiedByOwnerLabel", IDS_SETTINGS_USERS_MODIFIED_BY_OWNER_LABEL}, + {"guestBrowsingLabel", IDS_SETTINGS_USERS_GUEST_BROWSING_LABEL}, + {"settingsManagedLabel", IDS_SETTINGS_USERS_MANAGED_LABEL}, + {"showOnSigninLabel", IDS_SETTINGS_USERS_SHOW_ON_SIGNIN_LABEL}, + {"restrictSigninLabel", IDS_SETTINGS_USERS_RESTRICT_SIGNIN_LABEL}, + {"deviceOwnerLabel", IDS_SETTINGS_USERS_DEVICE_OWNER_LABEL}, + {"removeUserTooltip", IDS_SETTINGS_USERS_REMOVE_USER_TOOLTIP}, + {"addUsers", IDS_SETTINGS_USERS_ADD_USERS}, + {"addUsersEmail", IDS_SETTINGS_USERS_ADD_USERS_EMAIL}, + {"userExistsError", IDS_SETTINGS_USER_EXISTS_ERROR}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); +} + +void AddGoogleAssistantStrings(content::WebUIDataSource* html_source, + Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"googleAssistantPageTitle", IDS_SETTINGS_GOOGLE_ASSISTANT}, + {"googleAssistantEnableContext", IDS_ASSISTANT_SCREEN_CONTEXT_TITLE}, + {"googleAssistantEnableContextDescription", + IDS_ASSISTANT_SCREEN_CONTEXT_DESC}, + {"googleAssistantEnableHotword", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD}, + {"googleAssistantEnableHotwordDescription", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_DESCRIPTION}, + {"googleAssistantVoiceSettings", + IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS}, + {"googleAssistantVoiceSettingsDescription", + IDS_ASSISTANT_VOICE_MATCH_RECORDING}, + {"googleAssistantVoiceSettingsRetrainButton", + IDS_SETTINGS_GOOGLE_ASSISTANT_VOICE_SETTINGS_RETRAIN}, + {"googleAssistantEnableHotwordWithoutDspDescription", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_WITHOUT_DSP_DESCRIPTION}, + {"googleAssistantEnableHotwordWithoutDspRecommended", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_WITHOUT_DSP_RECOMMENDED}, + {"googleAssistantEnableHotwordWithoutDspAlwaysOn", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_WITHOUT_DSP_ALWAYS_ON}, + {"googleAssistantEnableHotwordWithoutDspOff", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_HOTWORD_WITHOUT_DSP_OFF}, + {"googleAssistantEnableNotification", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_NOTIFICATION}, + {"googleAssistantEnableNotificationDescription", + IDS_SETTINGS_GOOGLE_ASSISTANT_ENABLE_NOTIFICATION_DESCRIPTION}, + {"googleAssistantLaunchWithMicOpen", + IDS_SETTINGS_GOOGLE_ASSISTANT_LAUNCH_WITH_MIC_OPEN}, + {"googleAssistantLaunchWithMicOpenDescription", + IDS_SETTINGS_GOOGLE_ASSISTANT_LAUNCH_WITH_MIC_OPEN_DESCRIPTION}, + {"googleAssistantSettings", IDS_SETTINGS_GOOGLE_ASSISTANT_SETTINGS}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddBoolean("hotwordDspAvailable", + chromeos::IsHotwordDspAvailable()); + html_source->AddBoolean( + "voiceMatchDisabled", + chromeos::assistant::features::IsVoiceMatchDisabled()); +} + +void AddPrintingStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"printingPageTitle", IDS_SETTINGS_PRINTING}, + {"cupsPrintersTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTERS}, + {"cupsPrintersLearnMoreLabel", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_LEARN_MORE_LABEL}, + {"addCupsPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_PRINTER}, + {"editPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_EDIT}, + {"removePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTERS_REMOVE}, + {"setupPrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SETUP_BUTTON}, + {"setupPrinterAria", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_SETUP_BUTTON_ARIA}, + {"savePrinter", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SAVE_BUTTON}, + {"savePrinterAria", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SAVE_BUTTON_ARIA}, + {"searchLabel", IDS_SETTINGS_PRINTING_CUPS_SEARCH_LABEL}, + {"noSearchResults", IDS_SEARCH_NO_RESULTS}, + {"printerDetailsTitle", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_TITLE}, + {"printerName", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_NAME}, + {"printerModel", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_MODEL}, + {"printerQueue", IDS_SETTINGS_PRINTING_CUPS_PRINTER_DETAILS_QUEUE}, + {"savedPrintersTitle", IDS_SETTINGS_PRINTING_CUPS_SAVED_PRINTERS_TITLE}, + {"savedPrintersCountMany", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_SAVED_PRINTERS_COUNT_MANY}, + {"savedPrintersCountOne", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_SAVED_PRINTERS_COUNT_ONE}, + {"savedPrintersCountNone", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_SAVED_PRINTERS_COUNT_NONE}, + {"showMorePrinters", IDS_SETTINGS_PRINTING_CUPS_SHOW_MORE}, + {"addPrintersNearbyTitle", + IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTERS_NEARBY_TITLE}, + {"addPrintersManuallyTitle", + IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTERS_MANUALLY_TITLE}, + {"manufacturerAndModelDialogTitle", + IDS_SETTINGS_PRINTING_CUPS_SELECT_MANUFACTURER_AND_MODEL_TITLE}, + {"nearbyPrintersListTitle", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_AVAILABLE_PRINTERS}, + {"nearbyPrintersCountMany", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_AVAILABLE_PRINTERS_COUNT_MANY}, + {"nearbyPrintersCountOne", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_AVAILABLE_PRINTER_COUNT_ONE}, + {"nearbyPrintersCountNone", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_AVAILABLE_PRINTER_COUNT_NONE}, + {"nearbyPrintersListDescription", + IDS_SETTINGS_PRINTING_CUPS_PRINTERS_ADD_DETECTED_OR_NEW_PRINTER}, + {"manufacturerAndModelAdditionalInformation", + IDS_SETTINGS_PRINTING_CUPS_MANUFACTURER_MODEL_ADDITIONAL_INFORMATION}, + {"addPrinterButtonText", + IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_ADD}, + {"printerDetailsAdvanced", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED}, + {"printerDetailsA11yLabel", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_ACCESSIBILITY_LABEL}, + {"printerAddress", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_ADDRESS}, + {"printerProtocol", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_PROTOCOL}, + {"printerURI", IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADVANCED_URI}, + {"manuallyAddPrinterButtonText", + IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_MANUAL_ADD}, + {"discoverPrintersButtonText", + IDS_SETTINGS_PRINTING_CUPS_ADD_PRINTER_BUTTON_DISCOVER_PRINTERS}, + {"printerProtocolIpp", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_IPP}, + {"printerProtocolIpps", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_IPPS}, + {"printerProtocolHttp", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_HTTP}, + {"printerProtocolHttps", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_HTTPS}, + {"printerProtocolAppSocket", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_APP_SOCKET}, + {"printerProtocolLpd", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_LPD}, + {"printerProtocolUsb", IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_USB}, + {"printerProtocolIppUsb", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_PROTOCOL_IPPUSB}, + {"printerConfiguringMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_CONFIGURING_MESSAGE}, + {"printerManufacturer", IDS_SETTINGS_PRINTING_CUPS_PRINTER_MANUFACTURER}, + {"selectDriver", IDS_SETTINGS_PRINTING_CUPS_PRINTER_SELECT_DRIVER}, + {"selectDriverButtonText", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_BUTTON_SELECT_DRIVER}, + {"selectDriverButtonAriaLabel", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_BUTTON_SELECT_DRIVER_ARIA_LABEL}, + {"selectDriverErrorMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_INVALID_DRIVER}, + {"printerAddedSuccessfulMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_DONE_MESSAGE}, + {"printerEditedSuccessfulMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_EDITED_PRINTER_DONE_MESSAGE}, + {"printerUnavailableMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_UNAVAILABLE_MESSAGE}, + {"noPrinterNearbyMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_NO_PRINTER_NEARBY}, + {"searchingNearbyPrinters", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_SEARCHING_NEARBY_PRINTER}, + {"printerAddedFailedMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_ERROR_MESSAGE}, + {"printerAddedFatalErrorMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_FATAL_ERROR_MESSAGE}, + {"printerAddedUnreachableMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_PRINTER_UNREACHABLE_MESSAGE}, + {"printerAddedPpdTooLargeMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_PPD_TOO_LARGE_MESSAGE}, + {"printerAddedInvalidPpdMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_INVALID_PPD_MESSAGE}, + {"printerAddedPpdNotFoundMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_PPD_NOT_FOUND}, + {"printerAddedPpdUnretrievableMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_PRINTER_PPD_UNRETRIEVABLE}, + {"printerAddedNativePrintersNotAllowedMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_ADDED_NATIVE_PRINTERS_NOT_ALLOWED_MESSAGE}, + {"editPrinterInvalidPrinterUpdate", + IDS_SETTINGS_PRINTING_CUPS_EDIT_PRINTER_INVALID_PRINTER_UPDATE}, + {"requireNetworkMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_REQUIRE_INTERNET_MESSAGE}, + {"checkNetworkMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_CHECK_CONNECTION_MESSAGE}, + {"noInternetConnection", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_NO_INTERNET_CONNECTION}, + {"checkNetworkAndTryAgain", + IDS_SETTINGS_PRINTING_CUPS_PRINTER_CONNECT_TO_NETWORK_SUBTEXT}, + {"editPrinterDialogTitle", + IDS_SETTINGS_PRINTING_CUPS_EDIT_PRINTER_DIALOG_TITLE}, + {"editPrinterButtonText", IDS_SETTINGS_PRINTING_CUPS_EDIT_PRINTER_BUTTON}, + {"currentPpdMessage", + IDS_SETTINGS_PRINTING_CUPS_EDIT_PRINTER_CURRENT_PPD_MESSAGE}, + {"printerEulaNotice", IDS_SETTINGS_PRINTING_CUPS_EULA_NOTICE}, + {"ippPrinterUnreachable", IDS_SETTINGS_PRINTING_CUPS_IPP_URI_UNREACHABLE}, + {"generalPrinterDialogError", + IDS_SETTINGS_PRINTING_CUPS_DIALOG_GENERAL_ERROR}, + {"printServerButtonText", IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER}, + {"addPrintServerTitle", + IDS_SETTINGS_PRINTING_CUPS_ADD_PRINT_SERVER_TITLE}, + {"printServerAddress", IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_ADDRESS}, + {"printServerFoundZeroPrinters", + IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ZERO_PRINTERS}, + {"printServerFoundOnePrinter", + IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_ONE_PRINTER}, + {"printServerFoundManyPrinters", + IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_FOUND_MANY_PRINTERS}, + {"printServerInvalidUrlAddress", + IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_INVALID_URL_ADDRESS}, + {"printServerConnectionError", + IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_CONNECTION_ERROR}, + {"printServerConfigurationErrorMessage", + IDS_SETTINGS_PRINTING_CUPS_PRINT_SERVER_REACHABLE_BUT_CANNOT_ADD}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString("printingCUPSPrintLearnMoreUrl", + GetHelpUrlWithBoard(chrome::kCupsPrintLearnMoreURL)); + html_source->AddString( + "printingCUPSPrintPpdLearnMoreUrl", + GetHelpUrlWithBoard(chrome::kCupsPrintPPDLearnMoreURL)); + html_source->AddBoolean( + "consumerPrintServerUiEnabled", + base::FeatureList::IsEnabled(::features::kPrintServerUi)); +} + +void AddSearchInSettingsStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"searchPrompt", IDS_SETTINGS_SEARCH_PROMPT}, + {"searchNoResults", IDS_SEARCH_NO_RESULTS}, + {"searchResults", IDS_SEARCH_RESULTS}, + // TODO(dpapad): IDS_DOWNLOAD_CLEAR_SEARCH and IDS_HISTORY_CLEAR_SEARCH + // are identical, merge them to one and re-use here. + {"clearSearch", IDS_DOWNLOAD_CLEAR_SEARCH}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString( + "searchNoOsResultsHelp", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_SEARCH_NO_RESULTS_HELP, + base::ASCIIToUTF16(chrome::kOsSettingsSearchHelpURL))); + + html_source->AddBoolean( + "newOsSettingsSearch", + base::FeatureList::IsEnabled(chromeos::features::kNewOsSettingsSearch)); +} + +void AddDateTimeStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"dateTimePageTitle", IDS_SETTINGS_DATE_TIME}, + {"timeZone", IDS_SETTINGS_TIME_ZONE}, + {"selectTimeZoneResolveMethod", + IDS_SETTINGS_SELECT_TIME_ZONE_RESOLVE_METHOD}, + {"timeZoneGeolocation", IDS_SETTINGS_TIME_ZONE_GEOLOCATION}, + {"timeZoneButton", IDS_SETTINGS_TIME_ZONE_BUTTON}, + {"timeZoneSubpageTitle", IDS_SETTINGS_TIME_ZONE_SUBPAGE_TITLE}, + {"setTimeZoneAutomaticallyDisabled", + IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_DISABLED}, + {"setTimeZoneAutomaticallyOn", + IDS_SETTINGS_TIME_ZONE_DETECTION_SET_AUTOMATICALLY}, + {"setTimeZoneAutomaticallyOff", + IDS_SETTINGS_TIME_ZONE_DETECTION_CHOOSE_FROM_LIST}, + {"setTimeZoneAutomaticallyIpOnlyDefault", + IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_IP_ONLY_DEFAULT}, + {"setTimeZoneAutomaticallyWithWiFiAccessPointsData", + IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_SEND_WIFI_AP}, + {"setTimeZoneAutomaticallyWithAllLocationInfo", + IDS_SETTINGS_TIME_ZONE_DETECTION_MODE_SEND_ALL_INFO}, + {"use24HourClock", IDS_SETTINGS_USE_24_HOUR_CLOCK}, + {"setDateTime", IDS_SETTINGS_SET_DATE_TIME}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString( + "timeZoneSettingsLearnMoreURL", + base::ASCIIToUTF16(base::StringPrintf( + chrome::kTimeZoneSettingsLearnMoreURL, + g_browser_process->GetApplicationLocale().c_str()))); +} + +void AddAboutStrings(content::WebUIDataSource* html_source, Profile* profile) { + // Top level About page strings. + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"aboutProductLogoAlt", IDS_SHORT_PRODUCT_LOGO_ALT_TEXT}, +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + {"aboutReportAnIssue", IDS_SETTINGS_ABOUT_PAGE_REPORT_AN_ISSUE}, +#endif + {"aboutRelaunch", IDS_SETTINGS_ABOUT_PAGE_RELAUNCH}, + {"aboutUpgradeCheckStarted", IDS_SETTINGS_ABOUT_UPGRADE_CHECK_STARTED}, + {"aboutUpgradeRelaunch", IDS_SETTINGS_UPGRADE_SUCCESSFUL_RELAUNCH}, + {"aboutUpgradeUpdating", IDS_SETTINGS_UPGRADE_UPDATING}, + {"aboutUpgradeUpdatingPercent", IDS_SETTINGS_UPGRADE_UPDATING_PERCENT}, + {"aboutGetHelpUsingChrome", IDS_SETTINGS_GET_HELP_USING_CHROME}, + {"aboutPageTitle", IDS_SETTINGS_ABOUT_PROGRAM}, + {"aboutProductTitle", IDS_PRODUCT_NAME}, + + {"aboutEndOfLifeTitle", IDS_SETTINGS_ABOUT_PAGE_END_OF_LIFE_TITLE}, + {"aboutRelaunchAndPowerwash", + IDS_SETTINGS_ABOUT_PAGE_RELAUNCH_AND_POWERWASH}, + {"aboutRollbackInProgress", IDS_SETTINGS_UPGRADE_ROLLBACK_IN_PROGRESS}, + {"aboutRollbackSuccess", IDS_SETTINGS_UPGRADE_ROLLBACK_SUCCESS}, + {"aboutUpgradeUpdatingChannelSwitch", + IDS_SETTINGS_UPGRADE_UPDATING_CHANNEL_SWITCH}, + {"aboutUpgradeSuccessChannelSwitch", + IDS_SETTINGS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH}, + {"aboutTPMFirmwareUpdateTitle", + IDS_SETTINGS_ABOUT_TPM_FIRMWARE_UPDATE_TITLE}, + {"aboutTPMFirmwareUpdateDescription", + IDS_SETTINGS_ABOUT_TPM_FIRMWARE_UPDATE_DESCRIPTION}, + + // About page, channel switcher dialog. + {"aboutChangeChannel", IDS_SETTINGS_ABOUT_PAGE_CHANGE_CHANNEL}, + {"aboutChangeChannelAndPowerwash", + IDS_SETTINGS_ABOUT_PAGE_CHANGE_CHANNEL_AND_POWERWASH}, + {"aboutDelayedWarningMessage", + IDS_SETTINGS_ABOUT_PAGE_DELAYED_WARNING_MESSAGE}, + {"aboutDelayedWarningTitle", IDS_SETTINGS_ABOUT_PAGE_DELAYED_WARNING_TITLE}, + {"aboutPowerwashWarningMessage", + IDS_SETTINGS_ABOUT_PAGE_POWERWASH_WARNING_MESSAGE}, + {"aboutPowerwashWarningTitle", + IDS_SETTINGS_ABOUT_PAGE_POWERWASH_WARNING_TITLE}, + {"aboutUnstableWarningMessage", + IDS_SETTINGS_ABOUT_PAGE_UNSTABLE_WARNING_MESSAGE}, + {"aboutUnstableWarningTitle", + IDS_SETTINGS_ABOUT_PAGE_UNSTABLE_WARNING_TITLE}, + {"aboutChannelDialogBeta", IDS_SETTINGS_ABOUT_PAGE_DIALOG_CHANNEL_BETA}, + {"aboutChannelDialogDev", IDS_SETTINGS_ABOUT_PAGE_DIALOG_CHANNEL_DEV}, + {"aboutChannelDialogStable", IDS_SETTINGS_ABOUT_PAGE_DIALOG_CHANNEL_STABLE}, + + // About page, update warning dialog. + {"aboutUpdateWarningMessage", + IDS_SETTINGS_ABOUT_PAGE_UPDATE_WARNING_MESSAGE}, + {"aboutUpdateWarningTitle", IDS_SETTINGS_ABOUT_PAGE_UPDATE_WARNING_TITLE}, + + // Detailed build information + {"aboutBuildDetailsTitle", IDS_OS_SETTINGS_ABOUT_PAGE_BUILD_DETAILS}, + {"aboutChannelBeta", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_BETA}, + {"aboutChannelCanary", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_CANARY}, + {"aboutChannelDev", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_DEV}, + {"aboutChannelLabel", IDS_SETTINGS_ABOUT_PAGE_CHANNEL}, + {"aboutChannelStable", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL_STABLE}, + {"aboutCheckForUpdates", IDS_SETTINGS_ABOUT_PAGE_CHECK_FOR_UPDATES}, + {"aboutCurrentlyOnChannel", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL}, + {"aboutDetailedBuildInfo", IDS_SETTINGS_ABOUT_PAGE_DETAILED_BUILD_INFO}, + {version_ui::kApplicationLabel, IDS_PRODUCT_NAME}, + {version_ui::kPlatform, IDS_PLATFORM_LABEL}, + {version_ui::kFirmwareVersion, IDS_VERSION_UI_FIRMWARE_VERSION}, + {version_ui::kARC, IDS_ARC_LABEL}, + {"aboutBuildDetailsCopyTooltipLabel", + IDS_OS_SETTINGS_ABOUT_PAGE_BUILD_DETAILS_COPY_TOOLTIP_LABEL}, + {"aboutIsArcStatusTitle", IDS_OS_SETTINGS_ABOUT_ARC_STATUS_TITLE}, + {"aboutIsDeveloperModeTitle", IDS_OS_SETTINGS_ABOUT_DEVELOPER_MODE}, + {"isEnterpriseManagedTitle", + IDS_OS_SETTINGS_ABOUT_PAGE_ENTERPRISE_ENNROLLED_TITLE}, + {"aboutOsPageTitle", IDS_SETTINGS_ABOUT_OS}, + {"aboutGetHelpUsingChromeOs", IDS_SETTINGS_GET_HELP_USING_CHROME_OS}, + {"aboutOsProductTitle", IDS_PRODUCT_OS_NAME}, + {"aboutReleaseNotesOffline", IDS_SETTINGS_ABOUT_PAGE_RELEASE_NOTES}, + {"aboutShowReleaseNotes", IDS_SETTINGS_ABOUT_PAGE_SHOW_RELEASE_NOTES}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString("aboutTPMFirmwareUpdateLearnMoreURL", + chrome::kTPMFirmwareUpdateLearnMoreURL); + html_source->AddString( + "aboutUpgradeUpToDate", + ui::SubstituteChromeOSDeviceType(IDS_SETTINGS_UPGRADE_UP_TO_DATE)); + html_source->AddString("managementPage", + ManagementUI::GetManagementPageSubtitle(profile)); +} + +void AddResetStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"resetPageTitle", IDS_SETTINGS_RESET}, + {"powerwashTitle", IDS_SETTINGS_FACTORY_RESET}, + {"powerwashDialogTitle", IDS_SETTINGS_FACTORY_RESET_HEADING}, + {"powerwashDialogButton", IDS_SETTINGS_RESTART}, + {"powerwashButton", IDS_SETTINGS_FACTORY_RESET_BUTTON_LABEL}, + {"powerwashDialogExplanation", IDS_SETTINGS_FACTORY_RESET_WARNING}, + {"powerwashLearnMoreUrl", IDS_FACTORY_RESET_HELP_URL}, + {"powerwashButtonRoleDescription", + IDS_SETTINGS_FACTORY_RESET_BUTTON_ROLE}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString( + "powerwashDescription", + l10n_util::GetStringFUTF16(IDS_SETTINGS_FACTORY_RESET_DESCRIPTION, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); +} + +void AddSearchStrings(content::WebUIDataSource* html_source, Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"osSearchEngineLabel", IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL}, + {"searchGoogleAssistant", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT}, + {"searchGoogleAssistantEnabled", + IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ENABLED}, + {"searchGoogleAssistantDisabled", + IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_DISABLED}, + {"searchGoogleAssistantOn", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_ON}, + {"searchGoogleAssistantOff", IDS_SETTINGS_SEARCH_GOOGLE_ASSISTANT_OFF}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + // NOTE: This will be false when the flag is disabled. + const bool is_assistant_allowed = + ::assistant::IsAssistantAllowedForProfile(profile) == + ash::mojom::AssistantAllowedState::ALLOWED; + html_source->AddBoolean("isAssistantAllowed", is_assistant_allowed); + html_source->AddLocalizedString("osSearchPageTitle", + is_assistant_allowed + ? IDS_SETTINGS_SEARCH_AND_ASSISTANT + : IDS_SETTINGS_SEARCH); + html_source->AddString("searchExplanation", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_SEARCH_EXPLANATION, + base::ASCIIToUTF16(chrome::kOmniboxLearnMoreURL))); + html_source->AddString( + "osSearchEngineTooltip", + ui::SubstituteChromeOSDeviceType(IDS_OS_SETTINGS_SEARCH_ENGINE_TOOLTIP)); +} + +void AddPrivacyStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"privacyPageTitle", IDS_SETTINGS_PRIVACY}, + {"enableLogging", IDS_SETTINGS_ENABLE_LOGGING_PREF}, + {"enableLoggingDesc", IDS_SETTINGS_ENABLE_LOGGING_PREF_DESC}, + {"wakeOnWifi", IDS_SETTINGS_WAKE_ON_WIFI_DESCRIPTION}, + {"enableContentProtectionAttestation", + IDS_SETTINGS_ENABLE_CONTENT_PROTECTION_ATTESTATION}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddString("syncAndGoogleServicesLearnMoreURL", + chrome::kSyncAndGoogleServicesLearnMoreURL); + ::settings::AddPersonalizationOptionsStrings(html_source); +} + +void AddPeoplePageStrings(content::WebUIDataSource* html_source, + Profile* profile) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"osPeoplePageTitle", IDS_OS_SETTINGS_PEOPLE}, + {"accountManagerSubMenuLabel", + IDS_SETTINGS_ACCOUNT_MANAGER_SUBMENU_LABEL}, + {"accountManagerPageTitle", IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE}, + {"kerberosAccountsSubMenuLabel", + IDS_SETTINGS_KERBEROS_ACCOUNTS_SUBMENU_LABEL}, + {"accountManagerPageTitle", IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE}, + {"kerberosAccountsPageTitle", IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE}, + {"lockScreenFingerprintTitle", + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SUBPAGE_TITLE}, + {"manageOtherPeople", IDS_SETTINGS_PEOPLE_MANAGE_OTHER_PEOPLE}, + {"osSyncPageTitle", IDS_OS_SETTINGS_SYNC_PAGE_TITLE}, + {"syncAndNonPersonalizedServices", + IDS_SETTINGS_SYNC_SYNC_AND_NON_PERSONALIZED_SERVICES}, + {"syncDisconnectConfirm", IDS_SETTINGS_SYNC_DISCONNECT_CONFIRM}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + // Toggles the Chrome OS Account Manager submenu in the People section. + html_source->AddBoolean("isAccountManagerEnabled", + chromeos::IsAccountManagerAvailable(profile)); + + if (chromeos::features::IsSplitSyncConsentEnabled()) { + static constexpr webui::LocalizedString kTurnOffStrings[] = { + {"syncDisconnect", IDS_SETTINGS_PEOPLE_SYNC_TURN_OFF}, + {"syncDisconnectTitle", + IDS_SETTINGS_TURN_OFF_SYNC_AND_SIGN_OUT_DIALOG_TITLE}, + }; + AddLocalizedStringsBulk(html_source, kTurnOffStrings); + } else { + static constexpr webui::LocalizedString kSignOutStrings[] = { + {"syncDisconnect", IDS_SETTINGS_PEOPLE_SIGN_OUT}, + {"syncDisconnectTitle", IDS_SETTINGS_SYNC_DISCONNECT_TITLE}, + }; + AddLocalizedStringsBulk(html_source, kSignOutStrings); + } + + std::string sync_dashboard_url = + google_util::AppendGoogleLocaleParam( + GURL(chrome::kSyncGoogleDashboardURL), + g_browser_process->GetApplicationLocale()) + .spec(); + + html_source->AddString( + "syncDisconnectExplanation", + l10n_util::GetStringFUTF8(IDS_SETTINGS_SYNC_DISCONNECT_EXPLANATION, + base::ASCIIToUTF16(sync_dashboard_url))); + + AddAccountManagerPageStrings(html_source); + AddKerberosAccountsPageStrings(html_source); + AddKerberosAddAccountDialogStrings(html_source); + AddLockScreenPageStrings(html_source); + AddFingerprintListStrings(html_source); + AddFingerprintStrings(html_source); + AddSetupFingerprintDialogStrings(html_source); + AddSetupPinDialogStrings(html_source); + AddSyncControlsStrings(html_source); + + ::settings::AddSyncControlsStrings(html_source); + ::settings::AddSyncAccountControlStrings(html_source); + ::settings::AddPasswordPromptDialogStrings(html_source); + ::settings::AddSyncPageStrings(html_source); +} + +void AddPageVisibilityStrings(content::WebUIDataSource* html_source) { + PrefService* local_state = g_browser_process->local_state(); + // Toggles the Chrome OS Kerberos Accounts submenu in the People section. + // Note that the handler is also dependent on this pref. + html_source->AddBoolean("isKerberosEnabled", + local_state->GetBoolean(prefs::kKerberosEnabled)); +} + +} // namespace + +OsSettingsLocalizedStringsProvider::OsSettingsLocalizedStringsProvider( + Profile* profile, + local_search_service::mojom::LocalSearchService* local_search_service) { + local_search_service->GetIndex( + local_search_service::mojom::LocalSearchService::IndexId::CROS_SETTINGS, + index_remote_.BindNewPipeAndPassReceiver()); + + // Add per-page string providers. + // TODO(khorimoto): Add providers for the remaining pages. + per_page_providers_.push_back( + std::make_unique<InternetStringsProvider>(profile, /*delegate=*/this)); +} + +OsSettingsLocalizedStringsProvider::~OsSettingsLocalizedStringsProvider() = + default; + +void OsSettingsLocalizedStringsProvider::AddOsLocalizedStrings( + content::WebUIDataSource* html_source, + Profile* profile) { + for (const auto& per_page_provider : per_page_providers_) + per_page_provider->AddUiStrings(html_source); + + // TODO(khorimoto): Migrate these to OsSettingsPerPageStringsProvider + // instances. + AddAboutStrings(html_source, profile); + AddA11yStrings(html_source); + AddAndroidAppStrings(html_source); + AddAppManagementStrings(html_source); + AddAppsStrings(html_source); + AddBluetoothStrings(html_source); + AddChromeOSUserStrings(html_source, profile); + AddCommonStrings(html_source, profile); + AddCrostiniStrings(html_source, profile); + AddDateTimeStrings(html_source); + AddDeviceStrings(html_source); + AddFilesStrings(html_source); + AddGoogleAssistantStrings(html_source, profile); + AddLanguagesStrings(html_source); + AddMultideviceStrings(html_source); + AddParentalControlStrings(html_source, profile); + AddPageVisibilityStrings(html_source); + AddPeoplePageStrings(html_source, profile); + AddPersonalizationStrings(html_source); + AddPluginVmStrings(html_source, profile); + AddPrintingStrings(html_source); + AddPrivacyStrings(html_source); + AddResetStrings(html_source); + AddSearchInSettingsStrings(html_source); + AddSearchStrings(html_source, profile); + AddUsersStrings(html_source); + + policy_indicator::AddLocalizedStrings(html_source); + + html_source->UseStringsJs(); +} + +const SearchConcept* +OsSettingsLocalizedStringsProvider::GetCanonicalTagMetadata( + int canonical_message_id) const { + const auto it = canonical_id_to_metadata_map_.find(canonical_message_id); + if (it == canonical_id_to_metadata_map_.end()) + return nullptr; + return it->second; +} + +void OsSettingsLocalizedStringsProvider::Shutdown() { + index_remote_.reset(); +} + +void OsSettingsLocalizedStringsProvider::AddSearchTags( + const std::vector<SearchConcept>& tags_group) { + index_remote_->AddOrUpdate(ConceptVectorToDataPtrVector(tags_group), + /*callback=*/base::DoNothing()); + + // Add each concept to the map. Note that it is safe to take the address of + // each concept because all concepts are allocated via static + // base::NoDestructor objects in the Get*SearchConcepts() helper functions. + for (const auto& concept : tags_group) + canonical_id_to_metadata_map_[concept.canonical_message_id] = &concept; +} + +void OsSettingsLocalizedStringsProvider::RemoveSearchTags( + const std::vector<SearchConcept>& tags_group) { + std::vector<std::string> ids; + for (const auto& concept : tags_group) { + canonical_id_to_metadata_map_.erase(concept.canonical_message_id); + ids.push_back(base::NumberToString(concept.canonical_message_id)); + } + + index_remote_->Delete(ids, /*callback=*/base::DoNothing()); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h new file mode 100644 index 00000000000..c0d8c5adcd2 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h @@ -0,0 +1,91 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ + +#include <memory> +#include <unordered_map> +#include <vector> + +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.h" +#include "chrome/services/local_search_service/public/mojom/local_search_service.mojom.h" +#include "components/keyed_service/core/keyed_service.h" +#include "mojo/public/cpp/bindings/remote.h" + +class Profile; + +namespace content { +class WebUIDataSource; +} // namespace content + +namespace chromeos { +namespace settings { + +struct SearchConcept; + +// Provides two types of localized strings for OS settings: +// +// (1) UI strings: Strings displayed in the normal settings UI. This contains +// strings such as headers, labels, instructional notes, etc. These strings +// are added directly to the settings app's WebUIDataSource before the app +// starts up via the static AddOsLocalizedStrings() function and are +// accessible within settings via loadTimeData. +// +// (2) Search tags: Strings used as potential matches for user search queries +// within settings. These strings don't appear in the normal UI; instead, +// they specify actions which can be taken in settings. When a user types a +// search query in settings, we compare the query against these strings to +// look for potential matches. For each potential search result, there is a +// "canonical" tag which represents a common phrase, and zero or more +// alternate phrases (e.g., canonical: "Display settings", alternate: +// "Monitor settings"). +// +// Since some of the settings sections may be unavailable (e.g., we don't +// show Bluetooth settings unless the device has Bluetooth capabilities), +// these strings are added/removed according to the Add/Remove*SearchTags() +// instance functions. +class OsSettingsLocalizedStringsProvider + : public KeyedService, + public OsSettingsPerPageStringsProvider::Delegate { + public: + OsSettingsLocalizedStringsProvider( + Profile* profile, + local_search_service::mojom::LocalSearchService* local_search_service); + OsSettingsLocalizedStringsProvider( + const OsSettingsLocalizedStringsProvider& other) = delete; + OsSettingsLocalizedStringsProvider& operator=( + const OsSettingsLocalizedStringsProvider& other) = delete; + ~OsSettingsLocalizedStringsProvider() override; + + // Adds the strings needed by the OS settings page to |html_source| + // This function causes |html_source| to expose a strings.js file from its + // source which contains a mapping from string's name to its translated value. + void AddOsLocalizedStrings(content::WebUIDataSource* html_source, + Profile* profile); + + // Returns the tag metadata associated with |canonical_message_id|, which must + // be one of the canonical IDS_SETTINGS_TAG_* identifiers used for a search + // tag. If no metadata is available or if |canonical_message_id| instead + // refers to an alternate tag's ID, null is returned. + const SearchConcept* GetCanonicalTagMetadata(int canonical_message_id) const; + + private: + // KeyedService: + void Shutdown() override; + + // OsSettingsPerPageStringsProvider::Delegate: + void AddSearchTags(const std::vector<SearchConcept>& tags_group) override; + void RemoveSearchTags(const std::vector<SearchConcept>& tags_group) override; + + std::vector<std::unique_ptr<OsSettingsPerPageStringsProvider>> + per_page_providers_; + mojo::Remote<local_search_service::mojom::Index> index_remote_; + std::unordered_map<int, const SearchConcept*> canonical_id_to_metadata_map_; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.cc new file mode 100644 index 00000000000..8375790e5ca --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.cc @@ -0,0 +1,66 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.h" + +#include "chrome/browser/local_search_service/local_search_service_proxy.h" +#include "chrome/browser/local_search_service/local_search_service_proxy_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace chromeos { +namespace settings { + +// static +OsSettingsLocalizedStringsProvider* +OsSettingsLocalizedStringsProviderFactory::GetForProfile(Profile* profile) { + return static_cast<OsSettingsLocalizedStringsProvider*>( + OsSettingsLocalizedStringsProviderFactory::GetInstance() + ->GetServiceForBrowserContext(profile, /*create=*/true)); +} + +// static +OsSettingsLocalizedStringsProviderFactory* +OsSettingsLocalizedStringsProviderFactory::GetInstance() { + return base::Singleton<OsSettingsLocalizedStringsProviderFactory>::get(); +} + +OsSettingsLocalizedStringsProviderFactory:: + OsSettingsLocalizedStringsProviderFactory() + : BrowserContextKeyedServiceFactory( + "OsSettingsLocalizedStringsProvider", + BrowserContextDependencyManager::GetInstance()) { + DependsOn( + local_search_service::LocalSearchServiceProxyFactory::GetInstance()); +} + +OsSettingsLocalizedStringsProviderFactory:: + ~OsSettingsLocalizedStringsProviderFactory() = default; + +KeyedService* +OsSettingsLocalizedStringsProviderFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + Profile* profile = Profile::FromBrowserContext(context); + return new OsSettingsLocalizedStringsProvider( + profile, + local_search_service::LocalSearchServiceProxyFactory::GetForProfile( + Profile::FromBrowserContext(profile)) + ->GetLocalSearchService()); +} + +bool OsSettingsLocalizedStringsProviderFactory::ServiceIsNULLWhileTesting() + const { + return true; +} + +content::BrowserContext* +OsSettingsLocalizedStringsProviderFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextOwnInstanceInIncognito(context); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.h new file mode 100644 index 00000000000..d1b7df331e2 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.h @@ -0,0 +1,47 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_FACTORY_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; + +namespace chromeos { +namespace settings { + +class OsSettingsLocalizedStringsProvider; + +class OsSettingsLocalizedStringsProviderFactory + : public BrowserContextKeyedServiceFactory { + public: + static OsSettingsLocalizedStringsProvider* GetForProfile(Profile* profile); + static OsSettingsLocalizedStringsProviderFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits< + OsSettingsLocalizedStringsProviderFactory>; + + OsSettingsLocalizedStringsProviderFactory(); + ~OsSettingsLocalizedStringsProviderFactory() override; + + OsSettingsLocalizedStringsProviderFactory( + const OsSettingsLocalizedStringsProviderFactory&) = delete; + OsSettingsLocalizedStringsProviderFactory& operator=( + const OsSettingsLocalizedStringsProviderFactory&) = delete; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + bool ServiceIsNULLWhileTesting() const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_FACTORY_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_unittest.cc new file mode 100644 index 00000000000..c8eb26b6746 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_unittest.cc @@ -0,0 +1,104 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h" + +#include "base/run_loop.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/services/local_search_service/local_search_service_impl.h" +#include "chrome/services/local_search_service/public/mojom/local_search_service.mojom-test-utils.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "chromeos/network/network_state_test_helper.h" +#include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h" +#include "content/public/test/browser_task_environment.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/shill/dbus-constants.h" + +namespace chromeos { +namespace settings { + +class OsSettingsLocalizedStringsProviderTest : public testing::Test { + protected: + OsSettingsLocalizedStringsProviderTest() + : profile_manager_(TestingBrowserProcess::GetGlobal()) {} + ~OsSettingsLocalizedStringsProviderTest() override = default; + + // testing::Test: + void SetUp() override { + ASSERT_TRUE(profile_manager_.SetUp()); + + provider_ = std::make_unique<OsSettingsLocalizedStringsProvider>( + profile_manager_.CreateTestingProfile("TestingProfile"), + &local_search_service_); + + local_search_service_.GetIndex( + local_search_service::mojom::LocalSearchService::IndexId::CROS_SETTINGS, + index_remote_.BindNewPipeAndPassReceiver()); + + // Allow asynchronous networking code to complete (networking functionality + // is tested below). + base::RunLoop().RunUntilIdle(); + } + + content::BrowserTaskEnvironment task_environment_; + TestingProfileManager profile_manager_; + chromeos::network_config::CrosNetworkConfigTestHelper network_config_helper_; + mojo::Remote<local_search_service::mojom::Index> index_remote_; + local_search_service::LocalSearchServiceImpl local_search_service_; + std::unique_ptr<OsSettingsLocalizedStringsProvider> provider_; +}; + +// To prevent this from becoming a change-detector test, this test simply +// verifies that when the provider starts up, it adds *some* strings without +// checking the exact number. It also checks one specific canonical tag. +TEST_F(OsSettingsLocalizedStringsProviderTest, WifiTags) { + uint64_t initial_num_items = 0; + local_search_service::mojom::IndexAsyncWaiter(index_remote_.get()) + .GetSize(&initial_num_items); + EXPECT_GT(initial_num_items, 0u); + + const SearchConcept* network_settings_concept = + provider_->GetCanonicalTagMetadata(IDS_SETTINGS_TAG_NETWORK_SETTINGS); + ASSERT_TRUE(network_settings_concept); + EXPECT_EQ(chrome::kNetworksSubPage, + network_settings_concept->url_path_with_parameters); + EXPECT_EQ(mojom::SearchResultIcon::kWifi, network_settings_concept->icon); + + // Ethernet is not present by default, so no Ethernet concepts have yet been + // added. + const SearchConcept* ethernet_settings_concept = + provider_->GetCanonicalTagMetadata(IDS_SETTINGS_TAG_ETHERNET_SETTINGS); + ASSERT_FALSE(ethernet_settings_concept); + + // Add Ethernet and let asynchronous handlers run. This should cause Ethernet + // tags to be added. + network_config_helper_.network_state_helper().device_test()->AddDevice( + "/device/stub_eth_device", shill::kTypeEthernet, "stub_eth_device"); + base::RunLoop().RunUntilIdle(); + + uint64_t num_items_after_adding_ethernet = 0; + local_search_service::mojom::IndexAsyncWaiter(index_remote_.get()) + .GetSize(&num_items_after_adding_ethernet); + EXPECT_GT(num_items_after_adding_ethernet, initial_num_items); + + ethernet_settings_concept = + provider_->GetCanonicalTagMetadata(IDS_SETTINGS_TAG_ETHERNET_SETTINGS); + ASSERT_TRUE(ethernet_settings_concept); + EXPECT_EQ(chrome::kEthernetSettingsSubPage, + ethernet_settings_concept->url_path_with_parameters); + EXPECT_EQ(mojom::SearchResultIcon::kEthernet, + ethernet_settings_concept->icon); +} + +// Note that other tests do not need to be added for different group of tags, +// since these tests would only be verifying the contents of +// os_settings_localized_strings_provider.cc. + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.cc new file mode 100644 index 00000000000..26ac31b8cf6 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.cc @@ -0,0 +1,32 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.h" + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "base/system/sys_info.h" + +namespace chromeos { +namespace settings { + +// static +base::string16 OsSettingsPerPageStringsProvider::GetHelpUrlWithBoard( + const std::string& original_url) { + return base::ASCIIToUTF16(original_url + + "&b=" + base::SysInfo::GetLsbReleaseBoard()); +} + +OsSettingsPerPageStringsProvider::~OsSettingsPerPageStringsProvider() = default; + +OsSettingsPerPageStringsProvider::OsSettingsPerPageStringsProvider( + Profile* profile, + Delegate* delegate) + : profile_(profile), delegate_(delegate) { + DCHECK(profile); + DCHECK(delegate); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.h new file mode 100644 index 00000000000..c2a6771c85b --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_per_page_strings_provider.h @@ -0,0 +1,73 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_PER_PAGE_STRINGS_PROVIDER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_PER_PAGE_STRINGS_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "base/strings/string16.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" + +class Profile; + +namespace content { +class WebUIDataSource; +} // namespace content + +namespace chromeos { +namespace settings { + +// Provides strings for an individual page in OS settings (i.e., each subpage is +// expected to have its own implementation. Responsible for two types of +// strings: +// +// (1) UI strings: Strings (e.g., headers, labels) displayed in the settings UI. +// Added to a WebUIDataSource via the pure virtual AddUiStrings() function. +// +// (2) Search tags: Strings used as potential matches for user search queries +// within settings. Added/removed via the {Add|Remove}SearchTagsGroup +// delegate functions. Tags which are always searchable should be added in +// the class' constructor; however, tags which apply to content which is +// dynamically shown/hidden should be added when that content is visible and +// removed when the content is no longer visible. +class OsSettingsPerPageStringsProvider { + public: + class Delegate { + public: + ~Delegate() = default; + virtual void AddSearchTags( + const std::vector<SearchConcept>& tags_group) = 0; + virtual void RemoveSearchTags( + const std::vector<SearchConcept>& tags_group) = 0; + }; + + virtual ~OsSettingsPerPageStringsProvider(); + + OsSettingsPerPageStringsProvider( + const OsSettingsPerPageStringsProvider& other) = delete; + OsSettingsPerPageStringsProvider& operator=( + const OsSettingsPerPageStringsProvider& other) = delete; + + // Adds strings to be displayed in the UI via loadTimeData. + virtual void AddUiStrings(content::WebUIDataSource* html_source) const = 0; + + protected: + static base::string16 GetHelpUrlWithBoard(const std::string& original_url); + + OsSettingsPerPageStringsProvider(Profile* profile, Delegate* delegate); + + Profile* profile() { return profile_; } + Delegate* delegate() { return delegate_; } + + private: + Profile* profile_; + Delegate* delegate_; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_PER_PAGE_STRINGS_PROVIDER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc index a91606bde11..dd4edc8807a 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc @@ -11,14 +11,32 @@ #include <utility> #include <vector> +#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/network_config_service.h" #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h" +#include "ash/public/cpp/stylus_utils.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/feature_list.h" +#include "base/memory/ptr_util.h" +#include "base/metrics/histogram_functions.h" #include "build/build_config.h" -#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_process_platform_part.h" +#include "chrome/browser/chromeos/account_manager/account_manager_util.h" +#include "chrome/browser/chromeos/arc/arc_util.h" +#include "chrome/browser/chromeos/crostini/crostini_features.h" +#include "chrome/browser/chromeos/login/demo_mode/demo_session.h" +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" +#include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h" +#include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h" +#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/signin/identity_manager_factory.h" +#include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/webui/app_management/app_management.mojom.h" #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h" +#include "chrome/browser/ui/webui/chromeos/smb_shares/smb_handler.h" #include "chrome/browser/ui/webui/chromeos/sync/os_sync_handler.h" #include "chrome/browser/ui/webui/managed_ui_handler.h" #include "chrome/browser/ui/webui/metrics_handler.h" @@ -26,7 +44,33 @@ #include "chrome/browser/ui/webui/settings/about_handler.h" #include "chrome/browser/ui/webui/settings/accessibility_main_handler.h" #include "chrome/browser/ui/webui/settings/browser_lifetime_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/accessibility_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/crostini_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/date_time_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_keyboard_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_pointer_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_power_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/device_stylus_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/internet_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.h" #include "chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/pref_names.h" +#include "chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.h" #include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h" #include "chrome/browser/ui/webui/settings/downloads_handler.h" #include "chrome/browser/ui/webui/settings/extension_control_handler.h" @@ -40,7 +84,9 @@ #include "chrome/browser/ui/webui/settings/settings_cookies_view_handler.h" #include "chrome/browser/ui/webui/settings/settings_localized_strings_provider.h" #include "chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h" -#include "chrome/browser/ui/webui/settings/settings_ui.h" +#include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h" +#include "chrome/browser/ui/webui/settings/tts_handler.h" +#include "chrome/browser/ui/webui/webui_util.h" #include "chrome/browser/web_applications/system_web_app_manager.h" #include "chrome/common/chrome_features.h" #include "chrome/common/webui_url_constants.h" @@ -48,17 +94,36 @@ #include "chrome/grit/generated_resources.h" #include "chrome/grit/os_settings_resources.h" #include "chrome/grit/os_settings_resources_map.h" +#include "chromeos/components/account_manager/account_manager.h" +#include "chromeos/components/account_manager/account_manager_factory.h" +#include "chromeos/components/web_applications/manifest_request_filter.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/constants/chromeos_pref_names.h" +#include "chromeos/login/auth/password_visibility_utils.h" +#include "chromeos/services/multidevice_setup/public/cpp/prefs.h" #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" #include "components/password_manager/core/common/password_manager_features.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_service.h" #include "content/public/browser/web_ui_data_source.h" +#include "media/base/media_switches.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "ui/base/webui/web_ui_util.h" +#include "ui/chromeos/resources/grit/ui_chromeos_resources.h" +#include "ui/resources/grit/webui_resources.h" namespace chromeos { namespace settings { +// static +void OSSettingsUI::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref(prefs::kSyncOsWallpaper, false); +} + OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true), + time_when_opened_(base::TimeTicks::Now()), webui_load_timer_(web_ui->GetWebContents(), "ChromeOS.Settings.LoadDocumentTime", "ChromeOS.Settings.LoadCompletedTime") { @@ -66,7 +131,7 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) content::WebUIDataSource* html_source = content::WebUIDataSource::Create(chrome::kChromeUIOSSettingsHost); - ::settings::SettingsUI::InitOSWebUIHandlers(profile, web_ui, html_source); + InitOSWebUIHandlers(html_source); // This handler is for chrome://os-settings. html_source->AddBoolean("isOSSettings", true); @@ -78,6 +143,9 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) html_source->AddBoolean( "showParentalControls", chromeos::settings::ShouldShowParentalControls(profile)); + html_source->AddBoolean( + "syncSetupFriendlySettings", + base::FeatureList::IsEnabled(::features::kSyncSetupFriendlySettings)); AddSettingsPageUIHandler( std::make_unique<::settings::AccessibilityMainHandler>()); @@ -103,19 +171,17 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) std::make_unique<::settings::ProtocolHandlersHandler>()); AddSettingsPageUIHandler( std::make_unique<::settings::SearchEnginesHandler>(profile)); - AddSettingsPageUIHandler( - std::make_unique<chromeos::settings::WallpaperHandler>(web_ui)); html_source->AddBoolean("showAppManagement", base::FeatureList::IsEnabled( ::features::kAppManagement)); html_source->AddBoolean("splitSettingsSyncEnabled", chromeos::features::IsSplitSettingsSyncEnabled()); + html_source->AddBoolean("splitSyncConsent", + chromeos::features::IsSplitSyncConsentEnabled()); -#if defined(OS_CHROMEOS) html_source->AddBoolean( "isSupportedArcVersion", AppManagementPageHandler::IsCurrentArcVersionSupported(profile)); -#endif // OS_CHROMEOS AddSettingsPageUIHandler( base::WrapUnique(::settings::AboutHandler::Create(html_source, profile))); @@ -126,7 +192,6 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) web_ui->AddMessageHandler(std::make_unique<MetricsHandler>()); // Add the System Web App resources for Settings. - // TODO(jamescook|calamity): Migrate to chromeos::settings::OSSettingsUI. if (web_app::SystemWebAppManager::IsEnabled()) { html_source->AddResourcePath("icon-192.png", IDR_SETTINGS_LOGO_192); html_source->AddResourcePath("pwa.html", IDR_PWA_HTML); @@ -151,18 +216,37 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) #endif html_source->AddResourcePath("app-management/app_management.mojom-lite.js", - IDR_APP_MANAGEMENT_MOJO_LITE_JS); - html_source->AddResourcePath("app-management/types.mojom-lite.js", - IDR_APP_MANAGEMENT_TYPES_MOJO_LITE_JS); - html_source->AddResourcePath("app-management/bitmap.mojom-lite.js", - IDR_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS); - html_source->AddResourcePath("app-management/image.mojom-lite.js", - IDR_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS); - html_source->AddResourcePath("app-management/image_info.mojom-lite.js", - IDR_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS); - - ::settings::AddLocalizedStrings(html_source, profile, - web_ui->GetWebContents()); + IDR_OS_SETTINGS_APP_MANAGEMENT_MOJO_LITE_JS); + html_source->AddResourcePath( + "app-management/types.mojom-lite.js", + IDR_OS_SETTINGS_APP_MANAGEMENT_TYPES_MOJO_LITE_JS); + html_source->AddResourcePath( + "app-management/bitmap.mojom-lite.js", + IDR_OS_SETTINGS_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS); + html_source->AddResourcePath( + "app-management/file_path.mojom-lite.js", + IDR_OS_SETTINGS_APP_MANAGEMENT_FILE_PATH_MOJO_LITE_JS); + html_source->AddResourcePath( + "app-management/image.mojom-lite.js", + IDR_OS_SETTINGS_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS); + html_source->AddResourcePath( + "app-management/image_info.mojom-lite.js", + IDR_OS_SETTINGS_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS); + + html_source->AddResourcePath( + "search/user_action_recorder.mojom-lite.js", + IDR_OS_SETTINGS_USER_ACTION_RECORDER_MOJOM_LITE_JS); + html_source->AddResourcePath( + "search/search_result_icon.mojom-lite.js", + IDR_OS_SETTINGS_SEARCH_RESULT_ICON_MOJOM_LITE_JS); + html_source->AddResourcePath("search/search.mojom-lite.js", + IDR_OS_SETTINGS_SEARCH_MOJOM_LITE_JS); + + // AddOsLocalizedStrings must be added after AddBrowserLocalizedStrings + // as repeated keys used by the OS strings should override the same keys + // that may be used in the Browser string provider. + OsSettingsLocalizedStringsProviderFactory::GetForProfile(profile) + ->AddOsLocalizedStrings(html_source, profile); auto plural_string_handler = std::make_unique<PluralStringHandler>(); plural_string_handler->AddLocalizedString("profileLabel", @@ -173,16 +257,210 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(), html_source); +} - AddHandlerToRegistry(base::BindRepeating(&OSSettingsUI::BindCrosNetworkConfig, - base::Unretained(this))); - - AddHandlerToRegistry( - base::BindRepeating(&OSSettingsUI::BindAppManagementPageHandlerFactory, - base::Unretained(this))); +OSSettingsUI::~OSSettingsUI() { + // Note: OSSettingsUI lifetime is tied to the lifetime of the browser window. + base::UmaHistogramCustomTimes("ChromeOS.Settings.WindowOpenDuration", + base::TimeTicks::Now() - time_when_opened_, + /*min=*/base::TimeDelta::FromMicroseconds(500), + /*max=*/base::TimeDelta::FromHours(1), + /*buckets=*/50); } -OSSettingsUI::~OSSettingsUI() = default; +void OSSettingsUI::InitOSWebUIHandlers(content::WebUIDataSource* html_source) { + Profile* profile = Profile::FromWebUI(web_ui()); + + // TODO(jamescook): Sort out how account management is split between Chrome OS + // and browser settings. + if (chromeos::IsAccountManagerAvailable(profile)) { + chromeos::AccountManagerFactory* factory = + g_browser_process->platform_part()->GetAccountManagerFactory(); + chromeos::AccountManager* account_manager = + factory->GetAccountManager(profile->GetPath().value()); + DCHECK(account_manager); + + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::AccountManagerUIHandler>( + account_manager, IdentityManagerFactory::GetForProfile(profile))); + html_source->AddBoolean( + "secondaryGoogleAccountSigninAllowed", + profile->GetPrefs()->GetBoolean( + chromeos::prefs::kSecondaryGoogleAccountSigninAllowed)); + html_source->AddBoolean("isEduCoexistenceEnabled", + features::IsEduCoexistenceEnabled()); + } + + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::ChangePictureHandler>()); + + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::AccessibilityHandler>(profile)); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::AndroidAppsHandler>(profile)); + if (crostini::CrostiniFeatures::Get()->IsUIAllowed(profile, + /*check_policy=*/false)) { + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::CrostiniHandler>(profile)); + } + web_ui()->AddMessageHandler( + chromeos::settings::CupsPrintersHandler::Create(web_ui())); + web_ui()->AddMessageHandler(base::WrapUnique( + chromeos::settings::DateTimeHandler::Create(html_source))); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::FingerprintHandler>(profile)); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::GoogleAssistantHandler>(profile)); + + std::unique_ptr<chromeos::settings::KerberosAccountsHandler> + kerberos_accounts_handler = + chromeos::settings::KerberosAccountsHandler::CreateIfKerberosEnabled( + profile); + if (kerberos_accounts_handler) { + // Note that the UI is enabled only if Kerberos is enabled. + web_ui()->AddMessageHandler(std::move(kerberos_accounts_handler)); + } + + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::KeyboardHandler>()); + + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::WallpaperHandler>(web_ui())); + + // If |!allow_plugin_vm| we still want to |show_plugin_vm| if the VM image is + // on disk, so that users are still able to delete the image at will. + const bool allow_plugin_vm = plugin_vm::IsPluginVmAllowedForProfile(profile); + const bool show_plugin_vm = + allow_plugin_vm || + profile->GetPrefs()->GetBoolean(plugin_vm::prefs::kPluginVmImageExists); + + if (show_plugin_vm) { + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::PluginVmHandler>(profile)); + } + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::PointerHandler>()); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::QuickUnlockHandler>()); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::StorageHandler>(profile, + html_source)); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::StylusHandler>()); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::InternetHandler>(profile)); + web_ui()->AddMessageHandler(std::make_unique<::settings::TtsHandler>()); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::smb_dialog::SmbHandler>(profile, + base::DoNothing())); + + if (!profile->IsGuestSession()) { + chromeos::android_sms::AndroidSmsService* android_sms_service = + chromeos::android_sms::AndroidSmsServiceFactory::GetForBrowserContext( + profile); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::MultideviceHandler>( + profile->GetPrefs(), + chromeos::multidevice_setup::MultiDeviceSetupClientFactory:: + GetForProfile(profile), + android_sms_service + ? android_sms_service->android_sms_pairing_state_tracker() + : nullptr, + android_sms_service ? android_sms_service->android_sms_app_manager() + : nullptr)); + if (chromeos::settings::ShouldShowParentalControls(profile)) { + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::ParentalControlsHandler>( + profile)); + } + + if (chromeos::features::IsAmbientModeEnabled()) { + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::AmbientModeHandler>()); + } + } + + html_source->AddBoolean( + "privacySettingsRedesignEnabled", + base::FeatureList::IsEnabled(::features::kPrivacySettingsRedesign)); + + html_source->AddBoolean( + "multideviceAllowedByPolicy", + chromeos::multidevice_setup::AreAnyMultiDeviceFeaturesAllowed( + profile->GetPrefs())); + html_source->AddBoolean( + "quickUnlockEnabled", + chromeos::quick_unlock::IsPinEnabled(profile->GetPrefs())); + html_source->AddBoolean( + "quickUnlockDisabledByPolicy", + chromeos::quick_unlock::IsPinDisabledByPolicy(profile->GetPrefs())); + html_source->AddBoolean( + "userCannotManuallyEnterPassword", + !chromeos::password_visibility::AccountHasUserFacingPassword( + chromeos::ProfileHelper::Get() + ->GetUserByProfile(profile) + ->GetAccountId())); + const bool fingerprint_unlock_enabled = + chromeos::quick_unlock::IsFingerprintEnabled(profile); + html_source->AddBoolean("fingerprintUnlockEnabled", + fingerprint_unlock_enabled); + if (fingerprint_unlock_enabled) { + html_source->AddInteger( + "fingerprintReaderLocation", + static_cast<int32_t>(chromeos::quick_unlock::GetFingerprintLocation())); + + // To use lottie, the worker-src CSP needs to be updated for the web ui that + // is using it. Since as of now there are only a couple of webuis using + // lottie animations, this update has to be performed manually. As the usage + // increases, set this as the default so manual override is no longer + // required. + html_source->OverrideContentSecurityPolicyWorkerSrc( + "worker-src blob: 'self';"); + html_source->AddResourcePath("finger_print.json", + IDR_LOGIN_FINGER_PRINT_TABLET_ANIMATION); + } + html_source->AddBoolean("lockScreenNotificationsEnabled", + ash::features::IsLockScreenNotificationsEnabled()); + html_source->AddBoolean( + "lockScreenHideSensitiveNotificationsSupported", + ash::features::IsLockScreenHideSensitiveNotificationsSupported()); + html_source->AddBoolean("showTechnologyBadge", + !ash::features::IsSeparateNetworkIconsEnabled()); + html_source->AddBoolean("hasInternalStylus", + ash::stylus_utils::HasInternalStylus()); + + html_source->AddBoolean("showCrostini", + crostini::CrostiniFeatures::Get()->IsUIAllowed( + profile, /*check_policy=*/false)); + + html_source->AddBoolean( + "allowCrostini", crostini::CrostiniFeatures::Get()->IsUIAllowed(profile)); + + html_source->AddBoolean("allowPluginVm", allow_plugin_vm); + html_source->AddBoolean("showPluginVm", show_plugin_vm); + + html_source->AddBoolean("isDemoSession", + chromeos::DemoSession::IsDeviceInDemoMode()); + + // We have 2 variants of Android apps settings. Default case, when the Play + // Store app exists we show expandable section that allows as to + // enable/disable the Play Store and link to Android settings which is + // available once settings app is registered in the system. + // For AOSP images we don't have the Play Store app. In last case we Android + // apps settings consists only from root link to Android settings and only + // visible once settings app is registered. + html_source->AddBoolean("androidAppsVisible", + arc::IsArcAllowedForProfile(profile)); + html_source->AddBoolean("havePlayStoreApp", arc::IsPlayStoreAvailable()); + + html_source->AddBoolean("enablePowerSettings", true); + web_ui()->AddMessageHandler( + std::make_unique<chromeos::settings::PowerHandler>(profile->GetPrefs())); + + html_source->AddBoolean( + "showParentalControlsSettings", + chromeos::settings::ShouldShowParentalControls(profile)); +} void OSSettingsUI::AddSettingsPageUIHandler( std::unique_ptr<content::WebUIMessageHandler> handler) { @@ -190,12 +468,24 @@ void OSSettingsUI::AddSettingsPageUIHandler( web_ui()->AddMessageHandler(std::move(handler)); } -void OSSettingsUI::BindCrosNetworkConfig( +void OSSettingsUI::BindInterface( mojo::PendingReceiver<network_config::mojom::CrosNetworkConfig> receiver) { ash::GetNetworkConfigService(std::move(receiver)); } -void OSSettingsUI::BindAppManagementPageHandlerFactory( +void OSSettingsUI::BindInterface( + mojo::PendingReceiver<mojom::UserActionRecorder> receiver) { + user_action_recorder_ = + std::make_unique<SettingsUserActionTracker>(std::move(receiver)); +} + +void OSSettingsUI::BindInterface( + mojo::PendingReceiver<mojom::SearchHandler> receiver) { + SearchHandlerFactory::GetForProfile(Profile::FromWebUI(web_ui())) + ->BindInterface(std::move(receiver)); +} + +void OSSettingsUI::BindInterface( mojo::PendingReceiver<app_management::mojom::PageHandlerFactory> receiver) { if (!app_management_page_handler_factory_) { app_management_page_handler_factory_ = @@ -205,5 +495,7 @@ void OSSettingsUI::BindAppManagementPageHandlerFactory( app_management_page_handler_factory_->Bind(std::move(receiver)); } +WEB_UI_CONTROLLER_TYPE_IMPL(OSSettingsUI) + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h index ff325c20bc3..c72c6b57c22 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h @@ -8,40 +8,75 @@ #include <memory> #include "base/macros.h" +#include "base/time/time.h" #include "chrome/browser/ui/webui/app_management/app_management.mojom-forward.h" #include "chrome/browser/ui/webui/settings/chromeos/app_management/app_management_page_handler_factory.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom-forward.h" #include "chrome/browser/ui/webui/webui_load_timer.h" #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-forward.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "ui/webui/mojo_web_ui_controller.h" namespace content { +class WebUIDataSource; class WebUIMessageHandler; -} +} // namespace content + +namespace user_prefs { +class PrefRegistrySyncable; +} // namespace user_prefs namespace chromeos { namespace settings { +namespace mojom { +class SearchHandler; +} // namespace mojom + // The WebUI handler for chrome://os-settings. class OSSettingsUI : public ui::MojoWebUIController { public: + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + explicit OSSettingsUI(content::WebUI* web_ui); ~OSSettingsUI() override; - private: - void AddSettingsPageUIHandler( - std::unique_ptr<content::WebUIMessageHandler> handler); - void BindCrosNetworkConfig( + // Initializes the WebUI message handlers for OS-specific settings. + void InitOSWebUIHandlers(content::WebUIDataSource* html_source); + + // Instantiates implementor of the mojom::CrosNetworkConfig mojo interface + // passing the pending receiver that will be internally bound. + void BindInterface( mojo::PendingReceiver<network_config::mojom::CrosNetworkConfig> receiver); - void BindAppManagementPageHandlerFactory( + + // Instantiates implementor of the mojom::UserActionRecorder mojo interface + // passing the pending receiver that will be internally bound. + void BindInterface(mojo::PendingReceiver<mojom::UserActionRecorder> receiver); + + // Instantiates implementor of the mojom::SearchHandler mojo interface + // passing the pending receiver that will be internally bound. + void BindInterface(mojo::PendingReceiver<mojom::SearchHandler> receiver); + + // Instantiates implementor of the mojom::PageHandlerFactory mojo interface + // passing the pending receiver that will be internally bound. + void BindInterface( mojo::PendingReceiver<app_management::mojom::PageHandlerFactory> receiver); + private: + void AddSettingsPageUIHandler( + std::unique_ptr<content::WebUIMessageHandler> handler); + + base::TimeTicks time_when_opened_; + WebuiLoadTimer webui_load_timer_; + std::unique_ptr<mojom::UserActionRecorder> user_action_recorder_; std::unique_ptr<AppManagementPageHandlerFactory> app_management_page_handler_factory_; + WEB_UI_CONTROLLER_TYPE_DECL(); + DISALLOW_COPY_AND_ASSIGN(OSSettingsUI); }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc index 715782b0b11..db5d9d79e67 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc @@ -12,6 +12,8 @@ #include "base/bind_helpers.h" #include "chrome/browser/chromeos/file_manager/path_util.h" #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h" +#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h" +#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" @@ -32,6 +34,14 @@ void PluginVmHandler::RegisterMessages() { "removePluginVmSharedPath", base::BindRepeating(&PluginVmHandler::HandleRemovePluginVmSharedPath, weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "removePluginVm", + base::BindRepeating(&PluginVmHandler::HandleRemovePluginVm, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( + "requestPluginVmInstallerView", + base::BindRepeating(&PluginVmHandler::HandleRequestPluginVmInstallerView, + weak_ptr_factory_.GetWeakPtr())); } void PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText( @@ -68,5 +78,28 @@ void PluginVmHandler::HandleRemovePluginVmSharedPath( path)); } +void PluginVmHandler::HandleRemovePluginVm(const base::ListValue* args) { + CHECK_EQ(0U, args->GetSize()); + + auto* manager = plugin_vm::PluginVmManager::GetForProfile(profile_); + if (!manager) { + LOG(ERROR) << "removePluginVm called from an invalid profile."; + return; + } + manager->UninstallPluginVm(); +} + +void PluginVmHandler::HandleRequestPluginVmInstallerView( + const base::ListValue* args) { + CHECK(args->empty()); + + if (plugin_vm::IsPluginVmEnabled(profile_)) { + LOG(ERROR) << "requestPluginVmInstallerView called from a profile which " + "already has Plugin VM installed."; + return; + } + plugin_vm::ShowPluginVmInstallerView(profile_); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h index d57b34da1db..b003a9e3e11 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h @@ -32,6 +32,10 @@ class PluginVmHandler : public ::settings::SettingsPageUIHandler { void HandleGetPluginVmSharedPathsDisplayText(const base::ListValue* args); // Remove a specified path from being shared. void HandleRemovePluginVmSharedPath(const base::ListValue* args); + // Remove Plugin VM. + void HandleRemovePluginVm(const base::ListValue* args); + // Show the Plugin VM installer view if Plugin VM is not currently installed. + void HandleRequestPluginVmInstallerView(const base::ListValue* args); Profile* profile_; // weak_ptr_factory_ should always be last member. diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/pref_names.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/pref_names.cc new file mode 100644 index 00000000000..c36221f6270 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/pref_names.cc @@ -0,0 +1,20 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/pref_names.h" + +namespace chromeos { +namespace settings { +namespace prefs { + +// Boolean specifying whether OS wallpaper sync is enabled. This is stored +// separately from the other OS sync preferences because it's an edge case; +// wallpaper does not have its own ModelType, so it cannot be part of +// UserSelectableOsType like the other OS sync types. +// TODO(https://crbug.com/967987): Break this dependency. +const char kSyncOsWallpaper[] = "sync.os_wallpaper"; + +} // namespace prefs +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/pref_names.h b/chromium/chrome/browser/ui/webui/settings/chromeos/pref_names.h new file mode 100644 index 00000000000..df2e7dce599 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/pref_names.h @@ -0,0 +1,18 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_PREF_NAMES_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_PREF_NAMES_H_ + +namespace chromeos { +namespace settings { +namespace prefs { + +extern const char kSyncOsWallpaper[]; + +} // namespace prefs +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_PREF_NAMES_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.cc new file mode 100644 index 00000000000..f74ec2b9c89 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.cc @@ -0,0 +1,38 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/quick_unlock_handler.h" + +#include "base/bind.h" +#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" +#include "content/public/browser/web_ui.h" + +namespace chromeos { +namespace settings { + +QuickUnlockHandler::QuickUnlockHandler() = default; + +QuickUnlockHandler::~QuickUnlockHandler() = default; + +void QuickUnlockHandler::RegisterMessages() { + web_ui()->RegisterMessageCallback( + "RequestPinLoginState", + base::BindRepeating(&QuickUnlockHandler::HandleRequestPinLoginState, + base::Unretained(this))); +} + +void QuickUnlockHandler::HandleRequestPinLoginState( + const base::ListValue* args) { + AllowJavascript(); + quick_unlock::PinBackend::GetInstance()->HasLoginSupport( + base::BindOnce(&QuickUnlockHandler::OnPinLoginAvailable, + weak_ptr_factory_.GetWeakPtr())); +} + +void QuickUnlockHandler::OnPinLoginAvailable(bool is_available) { + FireWebUIListener("pin-login-available-changed", base::Value(is_available)); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.h new file mode 100644 index 00000000000..4796c99ce3c --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.h @@ -0,0 +1,42 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_QUICK_UNLOCK_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_QUICK_UNLOCK_HANDLER_H_ + +#include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" + +namespace base { +class ListValue; +} // namespace base + +namespace chromeos { +namespace settings { + +// Settings WebUI handler for quick unlock settings. +class QuickUnlockHandler : public ::settings::SettingsPageUIHandler { + public: + QuickUnlockHandler(); + QuickUnlockHandler(const QuickUnlockHandler&) = delete; + QuickUnlockHandler& operator=(const QuickUnlockHandler&) = delete; + ~QuickUnlockHandler() override; + + // SettingsPageUIHandler: + void RegisterMessages() override; + void OnJavascriptAllowed() override {} + void OnJavascriptDisallowed() override {} + + private: + void HandleRequestPinLoginState(const base::ListValue* args); + + void OnPinLoginAvailable(bool is_available); + + base::WeakPtrFactory<QuickUnlockHandler> weak_ptr_factory_{this}; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_QUICK_UNLOCK_HANDLER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/BUILD.gn b/chromium/chrome/browser/ui/webui/settings/chromeos/search/BUILD.gn new file mode 100644 index 00000000000..b4554acf90b --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2020 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. + +import("//mojo/public/tools/bindings/mojom.gni") + +assert(is_chromeos) + +mojom("mojo_bindings") { + sources = [ + "search.mojom", + "search_result_icon.mojom", + "user_action_recorder.mojom", + ] + + public_deps = [ "//mojo/public/mojom/base" ] +} diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/OWNERS b/chromium/chrome/browser/ui/webui/settings/chromeos/search/OWNERS new file mode 100644 index 00000000000..2d3d2668edc --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/OWNERS @@ -0,0 +1,7 @@ +khorimoto@chromium.org +zentaro@chromium.org + +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS + +# COMPONENT: OS>Systems>Settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom new file mode 100644 index 00000000000..d319e8d1079 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom @@ -0,0 +1,33 @@ +// Copyright 2020 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. + +module chromeos.settings.mojom; + +import "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom"; +import "mojo/public/mojom/base/string16.mojom"; + +// Search result metadata. +struct SearchResult { + // String to be displayed as a result in the UI. Meant to be displayed + // directly (i.e., not an ID but rather the actual text). + mojo_base.mojom.String16 result_text; + + // The URL path containing the relevant setting, which may or may not contain + // URL parameters. For example, the Wi-Fi list settings page is + // chrome://os-settings/networks?type=WiFi, so the field would be + // "networks?type=WiFi" for this page. + string url_path_with_parameters; + + // Icon to display for the search result. + SearchResultIcon icon; +}; + +// Provides settings search results. Implemented in the browser process; +// intended to be called from settings JS and Launcher C++. +interface SearchHandler { + // Searches settings for the given query and returns a list of results, sorted + // from most relevant to least relevant. An empty array indicates no relevant + // results. + Search(mojo_base.mojom.String16 query) => (array<SearchResult> results); +}; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h new file mode 100644 index 00000000000..f6dc3b8c6f6 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h @@ -0,0 +1,62 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_CONCEPT_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_CONCEPT_H_ + +#include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h" + +namespace chromeos { +namespace settings { + +// Represents a potential search result. In this context, "concept" refers to +// the fact that this search result represents an idea which may be described +// by more than just one phrase. For example, a concept of "Display settings" +// may also be described as "Monitor settings". +// +// Each concept has a canonical description search tag as well as up to +// |kMaxAltTagsPerConcept| alternate descriptions search tags. +struct SearchConcept { + static constexpr size_t kMaxAltTagsPerConcept = 4; + static constexpr int kAltTagEnd = 0; + + SearchConcept(const SearchConcept& other) = default; + SearchConcept& operator=(const SearchConcept& other) = default; + + // Message ID (from os_settings_search_tag_strings.grdp) corresponding to the + // canonical search tag for this concept. + int canonical_message_id; + + // URL path corresponding to the settings subpage at which the user can + // change a setting associated with the tag. This string can also contain + // URL parameters. + // + // Example 1 - Display settings (chrome://os-settings/device/display): + // ==> "device/display". + // Example 2 - Wi-Fi settings (chrome://os-settings/networks?type=WiFi): + // ==> "networks?type=WiFi" + const char* url_path_with_parameters; + + // Icon to display for search results associated with this concept. + mojom::SearchResultIcon icon; + + // Alternate message IDs (from os_settings_search_tag_strings.grdp) + // corresponding to this concept. These IDs refer to messages which represent + // an alternate way of describing the same concept (e.g., "Monitor settings" + // is an alternate phrase for "Display settings"). + // + // This field provides up to |kMaxAltTagsPerConcept| alternate tags, but not + // all concepts will require this many. A value of kAltTagEnd is used to + // indicate that there are no further tags. + // + // Example 1 - Four alternate tags: [1234, 1235, 1236, 1237] + // Example 2 - Two alternate tags: [1234, 1235, kAltTagEnd, _] + // Example 3 - Zero alternate tags: [kAltTagEnd, _, _, _] + int alt_tag_ids[kMaxAltTagsPerConcept] = {kAltTagEnd}; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_CONCEPT_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc new file mode 100644 index 00000000000..a94db926427 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc @@ -0,0 +1,107 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/search/search_handler.h" + +#include "base/strings/string_number_conversions.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h" +#include "chrome/services/local_search_service/local_search_service_impl.h" +#include "chrome/services/local_search_service/public/mojom/types.mojom.h" +#include "ui/base/l10n/l10n_util.h" + +namespace chromeos { +namespace settings { +namespace { + +const int32_t kLocalSearchServiceMaxResults = 10; + +} // namespace + +// static +const size_t SearchHandler::kNumMaxResults = 5; + +SearchHandler::SearchHandler( + OsSettingsLocalizedStringsProvider* strings_provider, + local_search_service::LocalSearchServiceImpl* local_search_service) + : strings_provider_(strings_provider), + index_(local_search_service->GetIndexImpl( + local_search_service::IndexId::kCrosSettings)) {} + +SearchHandler::~SearchHandler() = default; + +void SearchHandler::BindInterface( + mojo::PendingReceiver<mojom::SearchHandler> pending_receiver) { + receivers_.Add(this, std::move(pending_receiver)); +} + +std::vector<mojom::SearchResultPtr> SearchHandler::Search( + const base::string16& query) { + std::vector<local_search_service::Result> local_search_service_results; + local_search_service::ResponseStatus response_status = index_->Find( + query, kLocalSearchServiceMaxResults, &local_search_service_results); + + if (response_status != local_search_service::ResponseStatus::kSuccess) { + LOG(ERROR) << "Cannot search; LocalSearchService returned " + << static_cast<int>(response_status) + << ". Returning empty results array."; + return {}; + } + + return GenerateSearchResultsArray(local_search_service_results); +} + +void SearchHandler::Search(const base::string16& query, + SearchCallback callback) { + std::move(callback).Run(Search(query)); +} + +std::vector<mojom::SearchResultPtr> SearchHandler::GenerateSearchResultsArray( + const std::vector<local_search_service::Result>& + local_search_service_results) { + std::vector<mojom::SearchResultPtr> search_results; + for (const auto& result : local_search_service_results) { + mojom::SearchResultPtr result_ptr = ResultToSearchResult(result); + if (result_ptr) + search_results.push_back(std::move(result_ptr)); + + // Limit the number of results to return. + if (search_results.size() == kNumMaxResults) + break; + } + + return search_results; +} + +mojom::SearchResultPtr SearchHandler::ResultToSearchResult( + const local_search_service::Result& result) { + int message_id; + + // The result's ID is expected to be a stringified int. + if (!base::StringToInt(result.id, &message_id)) + return nullptr; + + const SearchConcept* concept = + strings_provider_->GetCanonicalTagMetadata(message_id); + + // If the concept was not registered, no metadata is available. This can occur + // if the search tag was dynamically unregistered during the asynchronous + // Find() call. + if (!concept) + return nullptr; + + return mojom::SearchResult::New(l10n_util::GetStringUTF16(message_id), + concept->url_path_with_parameters, + concept->icon); +} + +void SearchHandler::Shutdown() { + strings_provider_ = nullptr; + index_ = nullptr; + receivers_.Clear(); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h new file mode 100644 index 00000000000..36df7ed4bb2 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h @@ -0,0 +1,77 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_H_ + +#include <vector> + +#include "base/optional.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h" +#include "chrome/services/local_search_service/index_impl.h" +#include "components/keyed_service/core/keyed_service.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace local_search_service { +class LocalSearchServiceImpl; +} // namespace local_search_service + +namespace chromeos { +namespace settings { + +class OsSettingsLocalizedStringsProvider; + +// Handles search queries for Chrome OS settings. Search() is expected to be +// invoked by the settings UI as well as the the Launcher search UI. Search +// results are obtained by matching the provided query against search tags +// indexed in the LocalSearchService and cross-referencing results with +// OsSettingsLocalizedStringsProvider. +// +// SearchHandler returns at most |kNumMaxResults| results; searches which do not +// provide any matches result in an empty results array. +class SearchHandler : public mojom::SearchHandler, public KeyedService { + public: + // Maximum number of results returned by a Search() call. + static const size_t kNumMaxResults; + + SearchHandler( + OsSettingsLocalizedStringsProvider* strings_provider, + local_search_service::LocalSearchServiceImpl* local_search_service); + ~SearchHandler() override; + + SearchHandler(const SearchHandler& other) = delete; + SearchHandler& operator=(const SearchHandler& other) = delete; + + void BindInterface( + mojo::PendingReceiver<mojom::SearchHandler> pending_receiver); + + // Synchronous search implementation (for in-process clients). + std::vector<mojom::SearchResultPtr> Search(const base::string16& query); + + // mojom::SearchHandler: + void Search(const base::string16& query, SearchCallback callback) override; + + private: + // KeyedService: + void Shutdown() override; + + std::vector<mojom::SearchResultPtr> GenerateSearchResultsArray( + const std::vector<local_search_service::Result>& + local_search_service_results); + mojom::SearchResultPtr ResultToSearchResult( + const local_search_service::Result& result); + + OsSettingsLocalizedStringsProvider* strings_provider_; + local_search_service::IndexImpl* index_; + + // Note: Expected to have multiple clients, so a ReceiverSet is used. + mojo::ReceiverSet<mojom::SearchHandler> receivers_; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.cc new file mode 100644 index 00000000000..fba87783738 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.cc @@ -0,0 +1,61 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/search/search_handler_factory.h" + +#include "chrome/browser/local_search_service/local_search_service_proxy.h" +#include "chrome/browser/local_search_service/local_search_service_proxy_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider_factory.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace chromeos { +namespace settings { + +// static +SearchHandler* SearchHandlerFactory::GetForProfile(Profile* profile) { + return static_cast<SearchHandler*>( + SearchHandlerFactory::GetInstance()->GetServiceForBrowserContext( + profile, /*create=*/true)); +} + +// static +SearchHandlerFactory* SearchHandlerFactory::GetInstance() { + return base::Singleton<SearchHandlerFactory>::get(); +} + +SearchHandlerFactory::SearchHandlerFactory() + : BrowserContextKeyedServiceFactory( + "SearchHandler", + BrowserContextDependencyManager::GetInstance()) { + DependsOn( + local_search_service::LocalSearchServiceProxyFactory::GetInstance()); + DependsOn(OsSettingsLocalizedStringsProviderFactory::GetInstance()); +} + +SearchHandlerFactory::~SearchHandlerFactory() = default; + +KeyedService* SearchHandlerFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + Profile* profile = Profile::FromBrowserContext(context); + return new SearchHandler( + OsSettingsLocalizedStringsProviderFactory::GetForProfile(profile), + local_search_service::LocalSearchServiceProxyFactory::GetForProfile( + Profile::FromBrowserContext(profile)) + ->GetLocalSearchServiceImpl()); +} + +bool SearchHandlerFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +content::BrowserContext* SearchHandlerFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextOwnInstanceInIncognito(context); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.h new file mode 100644 index 00000000000..9b69e5f161c --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_factory.h @@ -0,0 +1,45 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_FACTORY_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +class Profile; + +namespace chromeos { +namespace settings { + +class SearchHandler; + +// Factory for SearchHandler; available for incognito and multi-profile cases to +// support settings search in those contexts. +class SearchHandlerFactory : public BrowserContextKeyedServiceFactory { + public: + static SearchHandler* GetForProfile(Profile* profile); + static SearchHandlerFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits<SearchHandlerFactory>; + + SearchHandlerFactory(); + ~SearchHandlerFactory() override; + + SearchHandlerFactory(const SearchHandlerFactory&) = delete; + SearchHandlerFactory& operator=(const SearchHandlerFactory&) = delete; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + bool ServiceIsNULLWhileTesting() const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_HANDLER_FACTORY_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc new file mode 100644 index 00000000000..cc04fb87193 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc @@ -0,0 +1,78 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/search/search_handler.h" + +#include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_localized_strings_provider.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom-test-utils.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/services/local_search_service/local_search_service_impl.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "chromeos/services/network_config/public/cpp/cros_network_config_test_helper.h" +#include "content/public/test/browser_task_environment.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace settings { + +class SearchHandlerTest : public testing::Test { + protected: + SearchHandlerTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {} + ~SearchHandlerTest() override = default; + + // testing::Test: + void SetUp() override { + ASSERT_TRUE(profile_manager_.SetUp()); + + provider_ = std::make_unique<OsSettingsLocalizedStringsProvider>( + profile_manager_.CreateTestingProfile("TestingProfile"), + &local_search_service_); + + handler_ = std::make_unique<SearchHandler>(provider_.get(), + &local_search_service_); + handler_->BindInterface(handler_remote_.BindNewPipeAndPassReceiver()); + + // Allow asynchronous networking code to complete; specifically, let code + // which adds Wi-Fi to the system run so that Wi-Fi search tags can be + // queried below. + base::RunLoop().RunUntilIdle(); + } + + content::BrowserTaskEnvironment task_environment_; + TestingProfileManager profile_manager_; + chromeos::network_config::CrosNetworkConfigTestHelper network_config_helper_; + mojo::Remote<mojom::SearchHandler> handler_remote_; + local_search_service::LocalSearchServiceImpl local_search_service_; + std::unique_ptr<OsSettingsLocalizedStringsProvider> provider_; + std::unique_ptr<SearchHandler> handler_; +}; + +TEST_F(SearchHandlerTest, Success) { + // Search for "Wi-Fi". + std::vector<mojom::SearchResultPtr> search_results; + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("Wi-Fi"), &search_results); + + // Multiple results should be available, and they should have Wi-Fi metadata. + EXPECT_GT(search_results.size(), 0u); + for (const auto& result : search_results) { + EXPECT_EQ(chrome::kWiFiSettingsSubPage, result->url_path_with_parameters); + EXPECT_EQ(mojom::SearchResultIcon::kWifi, result->icon); + } +} + +TEST_F(SearchHandlerTest, NoResults) { + std::vector<mojom::SearchResultPtr> search_results; + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("QueryWithNoResults"), &search_results); + EXPECT_TRUE(search_results.empty()); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom new file mode 100644 index 00000000000..ea4605ed09f --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom @@ -0,0 +1,14 @@ +// Copyright 2020 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. + +module chromeos.settings.mojom; + +// Icon types associated with a settings search result. +enum SearchResultIcon { + kCellular, + kEthernet, + // Note: Wi-Fi icon is used by default for networking tasks which may not be + // specifically tied to Wi-Fi. + kWifi +};
\ No newline at end of file diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.cc new file mode 100644 index 00000000000..49b774039a7 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.cc @@ -0,0 +1,131 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/search/settings_user_action_tracker.h" + +#include "base/metrics/histogram_functions.h" + +namespace chromeos { +namespace settings { + +namespace { + +// The maximum amount of time that the settings window can be blurred to be +// considered short enough for the "first change" metric. +constexpr base::TimeDelta kShortBlurTimeLimit = base::TimeDelta::FromMinutes(1); + +// The minimum amount of time between a setting change and a subsequent setting +// change. If two changes occur les than this amount of time from each other, +// they are ignored by metrics. See https://crbug.com/1073714 for details. +constexpr base::TimeDelta kMinSubsequentChange = + base::TimeDelta::FromMilliseconds(200); + +// Min/max values for the duration metrics. Note that these values are tied to +// the metrics defined below; if these ever change, the metric names must also +// be updated. +constexpr base::TimeDelta kMinDurationMetric = + base::TimeDelta::FromMilliseconds(100); +constexpr base::TimeDelta kMaxDurationMetric = base::TimeDelta::FromMinutes(10); + +void LogDurationMetric(const char* metric_name, base::TimeDelta duration) { + base::UmaHistogramCustomTimes(metric_name, duration, kMinDurationMetric, + kMaxDurationMetric, /*buckets=*/50); +} + +} // namespace + +SettingsUserActionTracker::SettingsUserActionTracker( + mojo::PendingReceiver<mojom::UserActionRecorder> pending_receiver) + : SettingsUserActionTracker() { + receiver_.Bind(std::move(pending_receiver)); +} + +SettingsUserActionTracker::SettingsUserActionTracker() + : metric_start_time_(base::TimeTicks::Now()) {} + +SettingsUserActionTracker::~SettingsUserActionTracker() = default; + +void SettingsUserActionTracker::RecordPageFocus() { + if (last_blur_timestamp_.is_null()) + return; + + // Log the duration of being blurred. + const base::TimeDelta blurred_duration = + base::TimeTicks::Now() - last_blur_timestamp_; + LogDurationMetric("ChromeOS.Settings.BlurredWindowDuration", + blurred_duration); + + // If the window was blurred for more than |kShortBlurTimeLimit|, + // the user was away from the window for long enough that we consider the + // user coming back to the window a new session for the purpose of metrics. + if (blurred_duration >= kShortBlurTimeLimit) { + ResetMetricsCountersAndTimestamp(); + last_record_setting_changed_timestamp_ = base::TimeTicks(); + } +} + +void SettingsUserActionTracker::RecordPageBlur() { + last_blur_timestamp_ = base::TimeTicks::Now(); +} + +void SettingsUserActionTracker::RecordClick() { + ++num_clicks_since_start_time_; +} + +void SettingsUserActionTracker::RecordNavigation() { + ++num_navigations_since_start_time_; +} + +void SettingsUserActionTracker::RecordSearch() { + ++num_searches_since_start_time_; +} + +void SettingsUserActionTracker::RecordSettingChange() { + base::TimeTicks now = base::TimeTicks::Now(); + + if (!last_record_setting_changed_timestamp_.is_null()) { + // If it has been less than |kMinSubsequentChange| since the last recorded + // setting change, this change is discarded. See https://crbug.com/1073714 + // for details. + if (now - last_record_setting_changed_timestamp_ < kMinSubsequentChange) + return; + + base::UmaHistogramCounts1000( + "ChromeOS.Settings.NumClicksUntilChange.SubsequentChange", + num_clicks_since_start_time_); + base::UmaHistogramCounts1000( + "ChromeOS.Settings.NumNavigationsUntilChange.SubsequentChange", + num_navigations_since_start_time_); + base::UmaHistogramCounts1000( + "ChromeOS.Settings.NumSearchesUntilChange.SubsequentChange", + num_searches_since_start_time_); + LogDurationMetric("ChromeOS.Settings.TimeUntilChange.SubsequentChange", + now - metric_start_time_); + } else { + base::UmaHistogramCounts1000( + "ChromeOS.Settings.NumClicksUntilChange.FirstChange", + num_clicks_since_start_time_); + base::UmaHistogramCounts1000( + "ChromeOS.Settings.NumNavigationsUntilChange.FirstChange", + num_navigations_since_start_time_); + base::UmaHistogramCounts1000( + "ChromeOS.Settings.NumSearchesUntilChange.FirstChange", + num_searches_since_start_time_); + LogDurationMetric("ChromeOS.Settings.TimeUntilChange.FirstChange", + now - metric_start_time_); + } + + ResetMetricsCountersAndTimestamp(); + last_record_setting_changed_timestamp_ = now; +} + +void SettingsUserActionTracker::ResetMetricsCountersAndTimestamp() { + metric_start_time_ = base::TimeTicks::Now(); + num_clicks_since_start_time_ = 0u; + num_navigations_since_start_time_ = 0u; + num_searches_since_start_time_ = 0u; +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.h new file mode 100644 index 00000000000..f355bcd524e --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker.h @@ -0,0 +1,74 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SETTINGS_USER_ACTION_TRACKER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SETTINGS_USER_ACTION_TRACKER_H_ + +#include "base/time/time.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" + +namespace chromeos { +namespace settings { + +// Records user actions which measure the effort required to change a setting. +// This class is only meant to track actions from an individual settings +// session; if the settings window is closed and reopened again, a new instance +// should be created for that new session. +class SettingsUserActionTracker : public mojom::UserActionRecorder { + public: + explicit SettingsUserActionTracker( + mojo::PendingReceiver<mojom::UserActionRecorder> pending_receiver); + SettingsUserActionTracker(const SettingsUserActionTracker& other) = delete; + SettingsUserActionTracker& operator=(const SettingsUserActionTracker& other) = + delete; + ~SettingsUserActionTracker() override; + + // mojom::UserActionRecorder: + void RecordPageFocus() override; + void RecordPageBlur() override; + void RecordClick() override; + void RecordNavigation() override; + void RecordSearch() override; + void RecordSettingChange() override; + + private: + friend class SettingsUserActionTrackerTest; + + // For unit tests. + SettingsUserActionTracker(); + + void ResetMetricsCountersAndTimestamp(); + + // Time at which the last setting change metric was recorded since the window + // has been focused, or null if no setting change has been recorded since the + // window has been focused. Note that if the user blurs the window then + // refocuses it in less than a minute, this value remains non-null; i.e., it + // flips back to null only when the user has blurred the window for over a + // minute. + base::TimeTicks last_record_setting_changed_timestamp_; + + // Time at which recording the current metric has started. If + // |has_changed_setting_| is true, we're currently measuring the "subsequent + // setting change" metric; otherwise, we're measuring the "first setting + // change" metric. + base::TimeTicks metric_start_time_; + + // Counters associated with the current metric. + size_t num_clicks_since_start_time_ = 0u; + size_t num_navigations_since_start_time_ = 0u; + size_t num_searches_since_start_time_ = 0u; + + // The last time at which a page blur event was received; if no blur events + // have been received, this field is_null(). + base::TimeTicks last_blur_timestamp_; + + mojo::Receiver<mojom::UserActionRecorder> receiver_{this}; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SETTINGS_USER_ACTION_TRACKER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker_unittest.cc new file mode 100644 index 00000000000..74a0c9cb0b2 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/settings_user_action_tracker_unittest.cc @@ -0,0 +1,176 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/search/settings_user_action_tracker.h" + +#include "base/test/metrics/histogram_tester.h" +#include "base/test/task_environment.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace settings { + +class SettingsUserActionTrackerTest : public testing::Test { + protected: + SettingsUserActionTrackerTest() = default; + ~SettingsUserActionTrackerTest() override = default; + + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + base::HistogramTester histogram_tester_; + SettingsUserActionTracker tracker_; +}; + +TEST_F(SettingsUserActionTrackerTest, TestRecordMetrics) { + // Focus the page, perform some tasks, and change a setting. + tracker_.RecordPageFocus(); + tracker_.RecordClick(); + tracker_.RecordNavigation(); + tracker_.RecordSearch(); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10)); + tracker_.RecordSettingChange(); + + // The "first change" metrics should have been logged. + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.FirstChange", + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumNavigationsUntilChange.FirstChange", + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumSearchesUntilChange.FirstChange", + /*count=*/1); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.FirstChange", + /*sample=*/base::TimeDelta::FromSeconds(10), + /*count=*/1); + + // Without leaving the page, perform some more tasks, and change another + // setting. + tracker_.RecordClick(); + tracker_.RecordNavigation(); + tracker_.RecordSearch(); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10)); + tracker_.RecordSettingChange(); + + // The "subsequent change" metrics should have been logged. + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumNavigationsUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumSearchesUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.SubsequentChange", + /*sample=*/base::TimeDelta::FromSeconds(10), + /*count=*/1); + + // Repeat this, but only after 100ms. This is lower than the minimum value + // required for this metric, so it should be ignored. + tracker_.RecordClick(); + tracker_.RecordNavigation(); + tracker_.RecordSearch(); + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(100)); + tracker_.RecordSettingChange(); + + // No additional logging should have occurred, so make the same verifications + // as above. + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumNavigationsUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumSearchesUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.SubsequentChange", + /*sample=*/base::TimeDelta::FromSeconds(10), + /*count=*/1); + + // Repeat this once more, and verify that the counts increased. + tracker_.RecordClick(); + tracker_.RecordNavigation(); + tracker_.RecordSearch(); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10)); + tracker_.RecordSettingChange(); + + // The "subsequent change" metrics should have been logged. + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.SubsequentChange", + /*count=*/2); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumNavigationsUntilChange.SubsequentChange", + /*count=*/2); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumSearchesUntilChange.SubsequentChange", + /*count=*/2); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.SubsequentChange", + /*sample=*/base::TimeDelta::FromSeconds(10), + /*count=*/2); +} + +TEST_F(SettingsUserActionTrackerTest, TestBlurAndFocus) { + // Focus the page, click, and change a setting. + tracker_.RecordPageFocus(); + tracker_.RecordClick(); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1)); + tracker_.RecordSettingChange(); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.FirstChange", + /*count=*/1); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.FirstChange", + /*sample=*/base::TimeDelta::FromSeconds(1), + /*count=*/1); + + // Blur for 59 seconds (not quite a minute), click, and change a setting. + // Since the blur was under a minute, this should count for the "subsequent + // change" metrics. + tracker_.RecordPageBlur(); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(59)); + tracker_.RecordPageFocus(); + tracker_.RecordClick(); + tracker_.RecordSettingChange(); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.BlurredWindowDuration", + /*sample=*/base::TimeDelta::FromSeconds(59), + /*count=*/1); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.SubsequentChange", + /*count=*/1); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.SubsequentChange", + /*sample=*/base::TimeDelta::FromSeconds(59), + /*count=*/1); + + // Now, blur for a full minute, click, and change a setting. Since the blur + // was a full minute, this should count for the "first change" metrics. + tracker_.RecordPageBlur(); + task_environment_.FastForwardBy(base::TimeDelta::FromMinutes(1)); + tracker_.RecordPageFocus(); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(5)); + tracker_.RecordClick(); + tracker_.RecordSettingChange(); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.BlurredWindowDuration", + /*sample=*/base::TimeDelta::FromMinutes(1), + /*count=*/2); + histogram_tester_.ExpectTotalCount( + "ChromeOS.Settings.NumClicksUntilChange.FirstChange", + /*count=*/2); + histogram_tester_.ExpectTimeBucketCount( + "ChromeOS.Settings.TimeUntilChange.FirstChange", + /*sample=*/base::TimeDelta::FromSeconds(5), + /*count=*/1); +} + +} // namespace settings. +} // namespace chromeos. diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom new file mode 100644 index 00000000000..1f151fc028d --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom @@ -0,0 +1,28 @@ +// Copyright 2020 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. + +module chromeos.settings.mojom; + +// Records user actions within OS settings. Implemented in the browser process; +// intended to be called from settings JS. +interface UserActionRecorder { + // Records that the settings window has been focused. + RecordPageFocus(); + + // Records that the settings window has been blurred (i.e., no longer + // focused). + RecordPageBlur(); + + // Records that the user has clicked within the settings page. + RecordClick(); + + // Records that the user has navigated to a settings subpage. + RecordNavigation(); + + // Records that the user has completed a search attempt. + RecordSearch(); + + // Records that the user has changed a setting. + RecordSettingChange(); +}; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.cc new file mode 100644 index 00000000000..005d006a42c --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.cc @@ -0,0 +1,73 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/server_printer_url_util.h" + +#include "url/gurl.h" + +namespace { + +// Returns an updated |gurl| with the specified components from the params. If +// |scheme| is not empty, returns an updated GURL with the specified scheme. If +// |replace_port| is true, returns an updated GURL with 631 as the port. 631 is +// the default port for IPP. +GURL UpdateServerPrinterGURL(const GURL& gurl, + const std::string& scheme, + bool replace_ipp_port) { + GURL::Replacements replacement; + if (!scheme.empty()) + replacement.SetSchemeStr(scheme); + if (replace_ipp_port) + replacement.SetPortStr("631"); + return gurl.ReplaceComponents(replacement); +} + +} // namespace + +namespace chromeos { +namespace settings { + +bool HasValidServerPrinterScheme(const GURL& gurl) { + return gurl.SchemeIsHTTPOrHTTPS() || gurl.SchemeIs("ipp") || + gurl.SchemeIs("ipps"); +} + +base::Optional<GURL> GenerateServerPrinterUrlWithValidScheme( + const std::string& url) { + base::Optional<GURL> gurl = base::make_optional(GURL(url)); + if (!HasValidServerPrinterScheme(*gurl)) { + // If we're missing a valid scheme, try querying with IPPS first. + gurl = GURL("ipps://" + url); + } + + if (!gurl->is_valid()) + return base::nullopt; + + // Replaces IPP/IPPS by HTTP/HTTPS. IPP standard describes protocol built + // on top of HTTP, so both types of addresses have the same meaning in the + // context of IPP interface. Moreover, the URL must have HTTP/HTTPS scheme + // to pass IsStandard() test from GURL library (see "Validation of the URL + // address" below). + if (gurl->SchemeIs("ipp")) { + gurl = UpdateServerPrinterGURL(*gurl, "http", + /*replace_ipp_port=*/false); + // The default port for IPP is 631. If the schema IPP is replaced by HTTP + // and the port is not explicitly defined in the URL, we have to overwrite + // the default HTTP port with the default IPP port. For IPPS we do nothing + // because implementers use the same port for IPPS and HTTPS. + if (gurl->IntPort() == url::PORT_UNSPECIFIED) { + gurl = UpdateServerPrinterGURL(*gurl, /*scheme=*/"", + /*replace_ipp_port=*/true); + } + } else if (gurl->SchemeIs("ipps")) { + gurl = UpdateServerPrinterGURL(*gurl, "https", + /*replace_ipp_port=*/false); + } + + // Check validation of the URL address and return |gurl| if valid. + return gurl->IsStandard() ? gurl : base::nullopt; +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h b/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h new file mode 100644 index 00000000000..ad118c14f4b --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util.h @@ -0,0 +1,30 @@ +// Copyright 2020 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SERVER_PRINTER_URL_UTIL_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SERVER_PRINTER_URL_UTIL_H_ + +#include <string> + +#include "base/optional.h" + +class GURL; + +namespace chromeos { +namespace settings { + +// Returns true if |gurl| has any the following scheme: HTTP, HTTPS, IPP, or +// IPPS. Returns false for an empty or any other scheme. +bool HasValidServerPrinterScheme(const GURL& gurl); + +// Returns a GURL from the input |url|. Returns base::nullopt if +// either |url| is invalid or constructing the GURL failed. This will also +// default the server printer URI to use HTTPS if it detects a missing scheme. +base::Optional<GURL> GenerateServerPrinterUrlWithValidScheme( + const std::string& url); + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SERVER_PRINTER_URL_UTIL_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.cc new file mode 100644 index 00000000000..26e2ec17433 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/server_printer_url_util_unittest.cc @@ -0,0 +1,89 @@ +// Copyright 2020 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/ui/webui/settings/chromeos/server_printer_url_util.h" + +#include <string> + +#include "base/optional.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace chromeos { +namespace settings { + +class ServerPrinterUrlUtilTest : public testing::Test { + public: + ServerPrinterUrlUtilTest() = default; + ~ServerPrinterUrlUtilTest() override = default; +}; + +TEST_F(ServerPrinterUrlUtilTest, IsValidScheme) { + GURL gurl1("ipp://123.123.11.11:123"); + ASSERT_TRUE(HasValidServerPrinterScheme(gurl1)); + + GURL gurl2("http://123.123.11.11:123"); + ASSERT_TRUE(HasValidServerPrinterScheme(gurl2)); + + GURL gurl3("ipps://123.123.11.11:123"); + ASSERT_TRUE(HasValidServerPrinterScheme(gurl3)); + + GURL gurl4("https://123.123.11.11:123"); + ASSERT_TRUE(HasValidServerPrinterScheme(gurl4)); + + // Missing scheme. + GURL gurl5("123.123.11.11:123"); + ASSERT_FALSE(HasValidServerPrinterScheme(gurl5)); + + // Invalid scheme. + GURL gurl6("test://123.123.11.11:123"); + ASSERT_FALSE(HasValidServerPrinterScheme(gurl6)); +} + +TEST_F(ServerPrinterUrlUtilTest, ConvertToGURL) { + // Test that a GURL is created with |gurl1| as its source. + std::string url1("http://123.123.11.11:631"); + base::Optional<GURL> gurl1 = GenerateServerPrinterUrlWithValidScheme(url1); + DCHECK(gurl1); + ASSERT_EQ("http://123.123.11.11:631/", gurl1->spec()); + ASSERT_EQ("http", gurl1->scheme()); + ASSERT_EQ("631", gurl1->port()); + + // Test that HTTPS is the default scheme if a scheme is not provided. + std::string url2("123.123.11.11:631"); + base::Optional<GURL> gurl2 = GenerateServerPrinterUrlWithValidScheme(url2); + DCHECK(gurl2); + ASSERT_EQ("https", gurl2->scheme()); + ASSERT_EQ("https://123.123.11.11:631/", gurl2->spec()); + + // Test that if a URL has IPP as its scheme, it will create a new GURL with + // HTTP as its scheme and 631 as its port. + std::string url3("ipp://123.123.11.11"); + base::Optional<GURL> gurl3 = GenerateServerPrinterUrlWithValidScheme(url3); + DCHECK(gurl3); + ASSERT_EQ("http", gurl3->scheme()); + ASSERT_EQ("631", gurl3->port()); + ASSERT_EQ("http://123.123.11.11:631/", gurl3->spec()); + + // Test that if a URL has IPP as its scheme and a specified port, it will + // create a new GURL with HTTP as the scheme and keeps the same port. + std::string url4("ipp://123.123.11.11:321"); + base::Optional<GURL> gurl4 = GenerateServerPrinterUrlWithValidScheme(url4); + DCHECK(gurl4); + ASSERT_EQ("http", gurl4->scheme()); + ASSERT_EQ("321", gurl4->port()); + ASSERT_EQ("http://123.123.11.11:321/", gurl4->spec()); + + // Test that if a URL has IPPS as its scheme and a specified port, a new GURL + // is created with the scheme as HTTPS and keeps the same port. + std::string url5("ipps://123.123.11.11:555"); + base::Optional<GURL> gurl5 = GenerateServerPrinterUrlWithValidScheme(url5); + DCHECK(gurl5); + ASSERT_EQ("https", gurl5->scheme()); + ASSERT_EQ("555", gurl5->port()); + ASSERT_EQ("https://123.123.11.11:555/", gurl5->spec()); +} + +} // namespace settings +} // namespace chromeos |