// 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.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/ui/webui/ash/settings/os_settings_ui.h"
#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 "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/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/privacy/app_permission_handler.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/browser/ui/webui/webui_util.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/base/ime/ash/input_method_manager.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/widget/widget.h"
#include "ui/webui/color_change_listener/color_change_handler.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;
}
};
} // 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);
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,
base::make_span(kOsSettingsResources, kOsSettingsResourcesSize),
IDR_OS_SETTINGS_OS_SETTINGS_HTML);
ash::EnableTrustedTypesCSP(html_source);
#if !BUILDFLAG(OPTIMIZE_WEBUI)
html_source->AddResourcePaths(
base::make_span(kSettingsSharedResources, kSettingsSharedResourcesSize));
#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<mojom::InputDeviceSettingsProvider> receiver) {
DCHECK(features::IsInputDeviceSettingsSplitEnabled());
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) {
CHECK(::features::IsShortcutCustomizationEnabled());
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) {
DCHECK(features::IsBluetoothDisconnectWarningEnabled());
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));
}
WEB_UI_CONTROLLER_TYPE_IMPL(OSSettingsUI)
} // namespace ash::settings