blob: bcb4954b305a725ea62324bbc1808f6055d33d21 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// 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/ash/settings/os_settings_ui.h"
#include <optional>
#include <string_view>
#include <utility>
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/audio_config_service.h"
#include "ash/public/cpp/bluetooth_config_service.h"
#include "ash/public/cpp/connectivity_services.h"
#include "ash/public/cpp/esim_manager.h"
#include "ash/public/cpp/hotspot_config_service.h"
#include "ash/public/cpp/network_config_service.h"
#include "ash/public/mojom/hid_preserving_bluetooth_state_controller.mojom.h"
#include "ash/system/bluetooth/hid_preserving_controller/hid_preserving_bluetooth_state_service.h"
#include "ash/webui/common/mojom/accelerator_fetcher.mojom.h"
#include "ash/webui/common/trusted_types_util.h"
#include "ash/webui/personalization_app/search/search.mojom.h"
#include "ash/webui/personalization_app/search/search_handler.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/login/quick_unlock/pin_backend.h"
#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_manager.h"
#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_manager_factory.h"
#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager.h"
#include "chrome/browser/nearby_sharing/nearby_receive_manager.h"
#include "chrome/browser/nearby_sharing/nearby_share_settings.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"
#include "chrome/browser/ui/webui/app_management/app_management_page_handler_base.h"
#include "chrome/browser/ui/webui/app_management/app_management_page_handler_factory.h"
#include "chrome/browser/ui/webui/ash/settings/pages/apps/app_notification_handler.h"
#include "chrome/browser/ui/webui/ash/settings/pages/apps/app_parental_controls_handler.h"
#include "chrome/browser/ui/webui/ash/settings/pages/people/graduation_handler.h"
#include "chrome/browser/ui/webui/ash/settings/pages/privacy/app_permission_handler.h"
#include "chrome/browser/ui/webui/ash/settings/pages/search/magic_boost_notice_page_handler_factory.h"
#include "chrome/browser/ui/webui/ash/settings/pages/storage/device_storage_handler.h"
#include "chrome/browser/ui/webui/ash/settings/pref_names.h"
#include "chrome/browser/ui/webui/ash/settings/search/search_handler.h"
#include "chrome/browser/ui/webui/ash/settings/services/hats/os_settings_hats_manager.h"
#include "chrome/browser/ui/webui/ash/settings/services/hats/os_settings_hats_manager_factory.h"
#include "chrome/browser/ui/webui/ash/settings/services/metrics/settings_user_action_tracker.h"
#include "chrome/browser/ui/webui/ash/settings/services/settings_manager/os_settings_manager.h"
#include "chrome/browser/ui/webui/ash/settings/services/settings_manager/os_settings_manager_factory.h"
#include "chrome/browser/ui/webui/managed_ui_handler.h"
#include "chrome/browser/ui/webui/sanitized_image_source.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/os_settings_resources.h"
#include "chrome/grit/os_settings_resources_map.h"
#include "chromeos/ash/services/auth_factor_config/in_process_instances.h"
#include "chromeos/ash/services/cellular_setup/cellular_setup_impl.h"
#include "chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom.h"
#include "chromeos/ash/services/ime/public/mojom/ime_service.mojom.h"
#include "chromeos/ash/services/ime/public/mojom/input_method_user_data.mojom.h"
#include "chromeos/components/in_session_auth/in_process_instances.h"
#include "chromeos/components/in_session_auth/mojom/in_session_auth.mojom.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_data_source.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/gfx/native_ui_types.h"
#include "ui/views/widget/widget.h"
#include "ui/webui/color_change_listener/color_change_handler.h"
#include "ui/webui/webui_util.h"
#if !BUILDFLAG(OPTIMIZE_WEBUI)
#include "chrome/grit/settings_shared_resources.h"
#include "chrome/grit/settings_shared_resources_map.h"
#endif
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "chrome/browser/nearby_sharing/internal/icons/grit/nearby_share_internal_icons.h"
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
namespace {
class AppManagementDelegate : public AppManagementPageHandlerBase::Delegate {
public:
AppManagementDelegate() = default;
~AppManagementDelegate() override = default;
gfx::NativeWindow GetUninstallAnchorWindow() const override {
return nullptr;
}
};
// Expects a path in the form of "jp-export-dictionary/123" where "123" is the
// dictionary id.
std::optional<uint64_t> ExtractJapaneseDictionaryExportIdParam(
const std::string& path) {
static constexpr std::string_view kJapaneseExportDictionaryPrefix =
"jp-export-dictionary/";
if (!path.starts_with(kJapaneseExportDictionaryPrefix)) {
return std::nullopt;
}
std::string_view dict_id_str = path;
dict_id_str.remove_prefix(kJapaneseExportDictionaryPrefix.size());
uint64_t dict_id;
if (!base::StringToUint64(dict_id_str, &dict_id)) {
return std::nullopt;
}
return dict_id;
}
// This function must be a non-member function because WebUIDataSource's
// lifetime is independent of OSSettingsUI's lifetime. In some cases the
// WebUIDataSource outlives OSSettingsUI and, in other cases, OSSettingsUI
// outlives the WebUIDataSource.
void OnHandleRequest(const std::string& path,
content::WebUIDataSource::GotDataCallback callback) {
std::optional<uint64_t> dict_id =
ExtractJapaneseDictionaryExportIdParam(path);
// Should not expect this to be called if the export request was not valid.
// Requests should have been filtered before.
CHECK(dict_id.has_value());
mojo::Remote<ash::ime::mojom::InputMethodUserDataService>
ime_user_data_service;
auto* ime_user_data_service_ptr = &ime_user_data_service;
ash::input_method::InputMethodManager::Get()->BindInputMethodUserDataService(
ime_user_data_service.BindNewPipeAndPassReceiver());
// Pass ime_user_data_service to the callback so that the Mojo connection does
// not get closed when the OnHandleRequest finishes.
ime_user_data_service_ptr->get()->ExportJapaneseDictionary(
*dict_id,
base::BindOnce(
[](mojo::Remote<ash::ime::mojom::InputMethodUserDataService> service,
content::WebUIDataSource::GotDataCallback callback,
const std::string& result) {
std::move(callback).Run(
base::MakeRefCounted<base::RefCountedString>(result));
},
std::move(ime_user_data_service), std::move(callback)));
}
} // namespace
namespace ash::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") {
Profile* profile = Profile::FromWebUI(web_ui);
content::WebUIDataSource* html_source =
content::WebUIDataSource::CreateAndAdd(profile,
chrome::kChromeUIOSSettingsHost);
html_source->SetRequestFilter(
base::BindRepeating([](const std::string& path) {
return ExtractJapaneseDictionaryExportIdParam(path).has_value();
}),
base::BindRepeating(OnHandleRequest));
content::URLDataSource::Add(profile,
std::make_unique<SanitizedImageSource>(profile));
OsSettingsManager* manager = OsSettingsManagerFactory::GetForProfile(profile);
manager->AddHandlers(web_ui);
manager->AddLoadTimeData(html_source);
// TODO(b/300151715): Move to StorageSection::AddHandler() once/if
// |html_source| parameter is removed.
web_ui->AddMessageHandler(
std::make_unique<StorageHandler>(profile, html_source));
webui::SetupWebUIDataSource(html_source, kOsSettingsResources,
IDR_OS_SETTINGS_OS_SETTINGS_HTML);
ash::EnableTrustedTypesCSP(html_source);
#if !BUILDFLAG(OPTIMIZE_WEBUI)
html_source->AddResourcePaths(kSettingsSharedResources);
#endif
// Flag for using updated icons in search results and pages.
html_source->AddBoolean("isNameEnabled", ::features::IsNameEnabled());
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
html_source->AddResourcePath("nearby/nearby-share-internal-icons.html",
IDR_NEARBY_SHARE_INTERNAL_ICONS);
html_source->AddResourcePath("nearby/nearby-share-internal-icons.m.js",
IDR_NEARBY_SHARE_INTERNAL_ICONS_M_JS);
#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
// 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->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::WorkerSrc,
"worker-src blob: chrome://resources 'self';");
ManagedUIHandler::Initialize(web_ui, html_source);
}
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::Microseconds(500),
/*max=*/base::Hours(1),
/*buckets=*/50);
// Sends request for OsSettingsHats notification upon shutting down the app.
OsSettingsHatsManager* settingsHatsManager =
OsSettingsHatsManagerFactory::GetInstance()->GetForProfile(
Profile::FromWebUI(web_ui()));
settingsHatsManager->MaybeSendSettingsHats();
// OsSettingsHatsManager records whether the user used the Search
// functionality per each session that Settings app has opened up. When the
// Settings app is closed, OsSettingsHatsManager will remain alive in the
// background and the state remains stored in the manager, so we will reset
// that knowledge.
settingsHatsManager->SetSettingsUsedSearch(false);
// Resets the tracking of device IDs associated with notification clicks.
// This method is called when the Settings app is closed to prevent the
// recording of metrics if a user changes settings long after clicking a
// notification.
InputDeviceSettingsController::Get()->ResetNotificationDeviceTracking();
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<cellular_setup::mojom::CellularSetup> receiver) {
cellular_setup::CellularSetupImpl::CreateAndBindToReciever(
std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<cellular_setup::mojom::ESimManager> receiver) {
GetESimManager(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<chromeos::network_config::mojom::CrosNetworkConfig>
receiver) {
GetNetworkConfigService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<mojom::UserActionRecorder> receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->settings_user_action_tracker()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<mojom::SearchHandler> receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->search_handler()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<personalization_app::mojom::SearchHandler> receiver) {
auto* profile = Profile::FromWebUI(web_ui());
DCHECK(personalization_app::CanSeeWallpaperOrPersonalizationApp(profile));
auto* search_handler = personalization_app::PersonalizationAppManagerFactory::
GetForBrowserContext(profile)
->search_handler();
DCHECK(search_handler);
search_handler->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<app_notification::mojom::AppNotificationsHandler>
receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->app_notification_handler()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<
app_parental_controls::mojom::AppParentalControlsHandler> receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->app_parental_controls_handler()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<app_permission::mojom::AppPermissionsHandler>
receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->app_permission_handler()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<app_management::mojom::PageHandlerFactory> receiver) {
if (!app_management_page_handler_factory_) {
auto delegate = std::make_unique<AppManagementDelegate>();
app_management_page_handler_factory_ =
std::make_unique<AppManagementPageHandlerFactory>(
Profile::FromWebUI(web_ui()), std::move(delegate));
}
app_management_page_handler_factory_->Bind(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<nearby_share::mojom::NearbyShareSettings> receiver) {
if (!NearbySharingServiceFactory::IsNearbyShareSupportedForBrowserContext(
Profile::FromWebUI(web_ui()))) {
return;
}
NearbySharingService* service =
NearbySharingServiceFactory::GetForBrowserContext(
Profile::FromWebUI(web_ui()));
service->GetSettings()->Bind(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<nearby_share::mojom::ReceiveManager> receiver) {
if (!NearbySharingServiceFactory::IsNearbyShareSupportedForBrowserContext(
Profile::FromWebUI(web_ui()))) {
return;
}
NearbySharingService* service =
NearbySharingServiceFactory::GetForBrowserContext(
Profile::FromWebUI(web_ui()));
mojo::MakeSelfOwnedReceiver(std::make_unique<NearbyReceiveManager>(service),
std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<nearby_share::mojom::ContactManager> receiver) {
if (!NearbySharingServiceFactory::IsNearbyShareSupportedForBrowserContext(
Profile::FromWebUI(web_ui()))) {
return;
}
NearbySharingService* service =
NearbySharingServiceFactory::GetForBrowserContext(
Profile::FromWebUI(web_ui()));
service->GetContactManager()->Bind(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<bluetooth_config::mojom::CrosBluetoothConfig>
receiver) {
GetBluetoothConfigService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<hotspot_config::mojom::CrosHotspotConfig> receiver) {
GetHotspotConfigService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<audio_config::mojom::CrosAudioConfig> receiver) {
GetAudioConfigService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<graduation::mojom::GraduationHandler> receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->graduation_handler()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<mojom::InputDeviceSettingsProvider> receiver) {
auto* provider =
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->input_device_settings_provider();
if (features::IsPeripheralCustomizationEnabled()) {
provider->Initialize(web_ui());
}
provider->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<mojom::DisplaySettingsProvider> receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->display_settings_provider()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<::ash::common::mojom::AcceleratorFetcher> receiver) {
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->accelerator_fetcher()
->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<::ash::common::mojom::ShortcutInputProvider>
receiver) {
CHECK(features::IsPeripheralCustomizationEnabled());
auto* shortcut_input_provider =
OsSettingsManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()))
->shortcut_input_provider();
auto* widget = views::Widget::GetWidgetForNativeWindow(
web_ui()->GetWebContents()->GetTopLevelNativeWindow());
if (widget) {
shortcut_input_provider->TieProviderToWidget(widget);
}
shortcut_input_provider->BindInterface(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
web_ui()->GetWebContents(), std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<auth::mojom::AuthFactorConfig> receiver) {
auth::BindToAuthFactorConfig(std::move(receiver),
quick_unlock::QuickUnlockFactory::GetDelegate(),
g_browser_process->local_state());
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<auth::mojom::RecoveryFactorEditor> receiver) {
auth::BindToRecoveryFactorEditor(
std::move(receiver), quick_unlock::QuickUnlockFactory::GetDelegate(),
g_browser_process->local_state());
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<auth::mojom::PinFactorEditor> receiver) {
auto* pin_backend = quick_unlock::PinBackend::GetInstance();
CHECK(pin_backend);
auth::BindToPinFactorEditor(std::move(receiver),
quick_unlock::QuickUnlockFactory::GetDelegate(),
g_browser_process->local_state(), *pin_backend);
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<auth::mojom::PasswordFactorEditor> receiver) {
auth::BindToPasswordFactorEditor(
std::move(receiver), quick_unlock::QuickUnlockFactory::GetDelegate(),
g_browser_process->local_state());
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<chromeos::auth::mojom::InSessionAuth> receiver) {
chromeos::auth::BindToInSessionAuthService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<google_drive::mojom::PageHandlerFactory> receiver) {
// The PageHandlerFactory is reused across same-origin navigations, so ensure
// any existing factories are reset.
google_drive_page_handler_factory_.reset();
google_drive_page_handler_factory_ =
std::make_unique<GoogleDrivePageHandlerFactory>(
Profile::FromWebUI(web_ui()), std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<one_drive::mojom::PageHandlerFactory> receiver) {
one_drive_page_handler_factory_ =
std::make_unique<OneDrivePageHandlerFactory>(Profile::FromWebUI(web_ui()),
std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<chromeos::connectivity::mojom::PasspointService>
receiver) {
ash::GetPasspointService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<ash::mojom::HidPreservingBluetoothStateController>
receiver) {
GetHidPreservingBluetoothStateControllerService(std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<ash::ime::mojom::InputMethodUserDataService>
receiver) {
input_method::InputMethodManager::Get()->BindInputMethodUserDataService(
std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<date_time::mojom::PageHandlerFactory> receiver) {
date_time_handler_factory_ = std::make_unique<DateTimeHandlerFactory>(
web_ui(), Profile::FromWebUI(web_ui()), std::move(receiver));
}
void OSSettingsUI::BindInterface(
mojo::PendingReceiver<magic_boost_handler::mojom::PageHandlerFactory>
receiver) {
magic_boost_notice_page_handler_factory_ =
std::make_unique<MagicBoostNoticePageHandlerFactory>(
Profile::FromWebUI(web_ui()), std::move(receiver));
}
WEB_UI_CONTROLLER_TYPE_IMPL(OSSettingsUI)
} // namespace ash::settings