chromium/chrome/browser/ui/webui/ash/settings/pages/crostini/crostini_section.cc

// Copyright 2020 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/pages/crostini/crostini_section.h"

#include "ash/components/arc/arc_prefs.h"
#include "ash/constants/ash_features.h"
#include "ash/webui/settings/public/constants/routes.mojom-forward.h"
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/bruschetta/bruschetta_util.h"
#include "chrome/browser/ash/crostini/crostini_disk.h"
#include "chrome/browser/ash/crostini/crostini_features.h"
#include "chrome/browser/ash/crostini/crostini_pref_names.h"
#include "chrome/browser/ash/crostini/crostini_util.h"
#include "chrome/browser/ash/guest_os/guest_id.h"
#include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/settings/pages/crostini/crostini_handler.h"
#include "chrome/browser/ui/webui/ash/settings/pages/crostini/guest_os_handler.h"
#include "chrome/browser/ui/webui/ash/settings/search/search_tag_registry.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/policy/core/common/management/management_service.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/text/bytes_formatting.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/chromeos/devicetype_utils.h"

namespace ash::settings {

namespace mojom {
using ::chromeos::settings::mojom::kAboutChromeOsSectionPath;
using ::chromeos::settings::mojom::kBruschettaDetailsSubpagePath;
using ::chromeos::settings::mojom::kBruschettaManageSharedFoldersSubpagePath;
using ::chromeos::settings::mojom::kBruschettaUsbPreferencesSubpagePath;
using ::chromeos::settings::mojom::kCrostiniBackupAndRestoreSubpagePath;
using ::chromeos::settings::mojom::kCrostiniDetailsSubpagePath;
using ::chromeos::settings::mojom::kCrostiniDevelopAndroidAppsSubpagePath;
using ::chromeos::settings::mojom::kCrostiniExtraContainersSubpagePath;
using ::chromeos::settings::mojom::kCrostiniManageSharedFoldersSubpagePath;
using ::chromeos::settings::mojom::kCrostiniPortForwardingSubpagePath;
using ::chromeos::settings::mojom::kCrostiniSectionPath;
using ::chromeos::settings::mojom::kCrostiniUsbPreferencesSubpagePath;
using ::chromeos::settings::mojom::Section;
using ::chromeos::settings::mojom::Setting;
using ::chromeos::settings::mojom::Subpage;
}  // namespace mojom

namespace {

const std::vector<SearchConcept>& GetCrostiniOptedInSearchConcepts() {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI,
       mojom::kCrostiniDetailsSubpagePath,
       mojom::SearchResultIcon::kDeveloperTags,
       mojom::SearchResultDefaultRank::kHigh,
       mojom::SearchResultType::kSubpage,
       {.subpage = mojom::Subpage::kCrostiniDetails},
       {IDS_OS_SETTINGS_TAG_CROSTINI_ALT1, IDS_OS_SETTINGS_TAG_CROSTINI_ALT2,
        SearchConcept::kAltTagEnd}},
      {IDS_OS_SETTINGS_TAG_CROSTINI_USB_PREFERENCES,
       mojom::kCrostiniUsbPreferencesSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSubpage,
       {.subpage = mojom::Subpage::kCrostiniUsbPreferences},
       {IDS_OS_SETTINGS_TAG_CROSTINI_USB_PREFERENCES_ALT1,
        SearchConcept::kAltTagEnd}},
      {IDS_OS_SETTINGS_TAG_CROSTINI_REMOVE,
       mojom::kCrostiniDetailsSubpagePath,
       mojom::SearchResultIcon::kDeveloperTags,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSetting,
       {.setting = mojom::Setting::kUninstallCrostini},
       {
           IDS_OS_SETTINGS_TAG_CROSTINI_REMOVE_ALT1,
           IDS_OS_SETTINGS_TAG_CROSTINI_REMOVE_ALT2,
           IDS_OS_SETTINGS_TAG_CROSTINI_REMOVE_ALT3,
           IDS_OS_SETTINGS_TAG_CROSTINI_REMOVE_ALT4,
           IDS_OS_SETTINGS_TAG_CROSTINI_REMOVE_ALT5,
       }},
      {IDS_OS_SETTINGS_TAG_CROSTINI_SHARED_FOLDERS,
       mojom::kCrostiniManageSharedFoldersSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSubpage,
       {.subpage = mojom::Subpage::kCrostiniManageSharedFolders},
       {IDS_OS_SETTINGS_TAG_CROSTINI_SHARED_FOLDERS_ALT1,
        IDS_OS_SETTINGS_TAG_CROSTINI_SHARED_FOLDERS_ALT2,
        IDS_OS_SETTINGS_TAG_CROSTINI_SHARED_FOLDERS_ALT3,
        SearchConcept::kAltTagEnd}},
      {IDS_OS_SETTINGS_TAG_CROSTINI_MIC_ACCESS,
       mojom::kCrostiniDetailsSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSetting,
       {.setting = mojom::Setting::kCrostiniMicAccess},
       {IDS_OS_SETTINGS_TAG_CROSTINI_MIC_ACCESS_ALT1,
        SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

const std::vector<SearchConcept>& GetCrostiniOptedOutSearchConcepts(
    mojom::Section section,
    const char* section_path) {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI,
       section_path,
       mojom::SearchResultIcon::kDeveloperTags,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSection,
       {.section = section},
       {IDS_OS_SETTINGS_TAG_CROSTINI_ALT1, IDS_OS_SETTINGS_TAG_CROSTINI_ALT2,
        SearchConcept::kAltTagEnd}},
      {IDS_OS_SETTINGS_TAG_CROSTINI_SETUP,
       section_path,
       mojom::SearchResultIcon::kDeveloperTags,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSetting,
       {.setting = mojom::Setting::kSetUpCrostini},
       {IDS_OS_SETTINGS_TAG_CROSTINI_SETUP_ALT1, SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

const std::vector<SearchConcept>& GetCrostiniExportImportSearchConcepts() {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI_BACKUP_RESTORE,
       mojom::kCrostiniBackupAndRestoreSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSubpage,
       {.subpage = mojom::Subpage::kCrostiniBackupAndRestore},
       {IDS_OS_SETTINGS_TAG_CROSTINI_BACKUP_RESTORE_ALT1,
        SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

const std::vector<SearchConcept>& GetCrostiniAdbSideloadingSearchConcepts() {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI_ANDROID_APPS_ADB,
       mojom::kCrostiniDevelopAndroidAppsSubpagePath,
       mojom::SearchResultIcon::kDeveloperTags,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSetting,
       {.setting = mojom::Setting::kCrostiniAdbDebugging},
       {IDS_OS_SETTINGS_TAG_CROSTINI_ANDROID_APPS_ADB_ALT1,
        SearchConcept::kAltTagEnd}},
      {IDS_OS_SETTINGS_TAG_CROSTINI_ANDROID_APPS,
       mojom::kCrostiniDevelopAndroidAppsSubpagePath,
       mojom::SearchResultIcon::kDeveloperTags,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSubpage,
       {.subpage = mojom::Subpage::kCrostiniDevelopAndroidApps},
       {IDS_OS_SETTINGS_TAG_CROSTINI_ANDROID_APPS_ALT1,
        SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

const std::vector<SearchConcept>& GetCrostiniPortForwardingSearchConcepts() {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI_PORT_FORWARDING,
       mojom::kCrostiniPortForwardingSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSubpage,
       {.subpage = mojom::Subpage::kCrostiniPortForwarding},
       {IDS_OS_SETTINGS_TAG_CROSTINI_PORT_FORWARDING_ALT1,
        SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

const std::vector<SearchConcept>& GetCrostiniContainerUpgradeSearchConcepts() {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI_CONTAINER_UPGRADE,
       mojom::kCrostiniDetailsSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSetting,
       {.setting = mojom::Setting::kCrostiniContainerUpgrade},
       {IDS_OS_SETTINGS_TAG_CROSTINI_CONTAINER_UPGRADE_ALT1,
        SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

const std::vector<SearchConcept>& GetCrostiniDiskResizingSearchConcepts() {
  static const base::NoDestructor<std::vector<SearchConcept>> tags({
      {IDS_OS_SETTINGS_TAG_CROSTINI_DISK_RESIZE,
       mojom::kCrostiniDetailsSubpagePath,
       mojom::SearchResultIcon::kPenguin,
       mojom::SearchResultDefaultRank::kMedium,
       mojom::SearchResultType::kSubpage,
       {.setting = mojom::Setting::kCrostiniDiskResize},
       {IDS_OS_SETTINGS_TAG_CROSTINI_DISK_RESIZE_ALT1,
        SearchConcept::kAltTagEnd}},
  });
  return *tags;
}

bool IsProfileManaged(Profile* profile) {
  return profile->GetProfilePolicyConnector()->IsManaged();
}

bool IsDeviceManaged() {
  return policy::ManagementServiceFactory::GetForPlatform()->IsManaged();
}

bool IsAdbSideloadingAllowed() {
  return base::FeatureList::IsEnabled(features::kArcAdbSideloadingFeature);
}

}  // namespace

CrostiniSection::CrostiniSection(Profile* profile,
                                 SearchTagRegistry* search_tag_registry,
                                 PrefService* pref_service)
    : OsSettingsSection(profile, search_tag_registry),
      pref_service_(pref_service),
      profile_(profile) {
  pref_change_registrar_.Init(pref_service_);
  pref_change_registrar_.Add(
      crostini::prefs::kUserCrostiniAllowedByPolicy,
      base::BindRepeating(&CrostiniSection::UpdateSearchTags,
                          base::Unretained(this)));
  pref_change_registrar_.Add(
      crostini::prefs::kCrostiniEnabled,
      base::BindRepeating(&CrostiniSection::UpdateSearchTags,
                          base::Unretained(this)));
  pref_change_registrar_.Add(
      crostini::prefs::kUserCrostiniExportImportUIAllowedByPolicy,
      base::BindRepeating(&CrostiniSection::UpdateSearchTags,
                          base::Unretained(this)));
  pref_change_registrar_.Add(
      arc::prefs::kArcEnabled,
      base::BindRepeating(&CrostiniSection::UpdateSearchTags,
                          base::Unretained(this)));
  UpdateSearchTags();
}

CrostiniSection::~CrostiniSection() = default;

bool CrostiniSection::ShouldShowBruschetta(Profile* profile) {
  const bool bru_installable =
      !bruschetta::GetInstallableConfigs(profile).empty();
  const bool bru_installed =
      !guest_os::GetContainers(profile, guest_os::VmType::BRUSCHETTA).empty();
  return bru_installable || bru_installed;
}

void CrostiniSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
  const bool kIsRevampEnabled =
      ash::features::IsOsSettingsRevampWayfindingEnabled();

  webui::LocalizedString kLocalizedStrings[] = {
      {"bruschettaPageLabel", IDS_SETTINGS_BRUSCHETTA_LABEL},
      {"bruschettaEnable", IDS_SETTINGS_TURN_ON},
      {"bruschettaMicDialogShutdownButton",
       IDS_SETTINGS_BRUSCHETTA_MIC_DIALOG_SHUTDOWN_BUTTON},
      {"bruschettaRemoveButton", IDS_SETTINGS_BRUSCHETTA_REMOVE_BUTTON},
      {"crostiniPageTitle", IDS_SETTINGS_CROSTINI_TITLE},
      {"crostiniPageLabel", IDS_SETTINGS_CROSTINI_LABEL},
      {"crostiniEnable", kIsRevampEnabled
                             ? IDS_OS_SETTINGS_REVAMP_CROSTINI_SET_UP
                             : IDS_SETTINGS_TURN_ON},
      {"crostiniSharedPathsInstructionsAdd",
       IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD},
      {"crostiniSharedPathsRemoveFailureDialogMessage",
       IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_FAILURE_DIALOG_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},
      {"crostiniSharedUsbDevicesDescription",
       IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_DESCRIPTION},
      {"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},
      {"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},
      {"crostiniPortForwardingAddPortDialogPortNumberLabel",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_PORT_NUMBER_LABEL},
      {"crostiniPortForwardingAddPortDialogLabelLabel",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL_LABEL},
      {"crostiniPortForwardingTCP", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP},
      {"crostiniPortForwardingUDP", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP},
      {"crostiniPortForwardingAddError",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_ERROR},
      {"crostiniPortForwardingAddExisting",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_EXISTING},
      {"crostiniPortForwardingRemoveAllPorts",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS},
      {"crostiniPortForwardingRemovePort",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_PORT},
      {"crostiniPortForwardingActivatePortError",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ACTIVATE_PORT_ERROR},
      {"crostiniPortForwardingToggleAriaLabel",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TOGGLE_PORT_ARIA_LABEL},
      {"crostiniPortForwardingRemoveAllPortsAriaLabel",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS_ARIA_LABEL},
      {"crostiniPortForwardingShowMoreActionsAriaLabel",
       IDS_SETTINGS_CROSTINI_PORT_FORWARDING_SHOW_MORE_ACTIONS_ARIA_LABEL},
      {"crostiniDiskResizeTitle", IDS_SETTINGS_CROSTINI_DISK_RESIZE_TITLE},
      {"crostiniDiskResizeShowButton",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_SHOW_BUTTON},
      {"crostiniDiskResizeShowButtonAriaLabel",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_SHOW_BUTTON_ARIA_LABEL},
      {"crostiniDiskSizeCalculating", IDS_SETTINGS_STORAGE_SIZE_CALCULATING},
      {"crostiniDiskReserveSizeButton",
       IDS_SETTINGS_CROSTINI_DISK_RESERVE_SIZE_BUTTON},
      {"crostiniDiskReserveSizeButtonAriaLabel",
       IDS_SETTINGS_CROSTINI_DISK_RESERVE_SIZE_BUTTON_ARIA_LABEL},
      {"crostiniDiskResizeLabel", IDS_SETTINGS_CROSTINI_DISK_RESIZE_LABEL},
      {"crostiniDiskResizeDynamicallyAllocatedSubtext",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_DYNAMICALLY_ALLOCATED_SUBTEXT},
      {"crostiniDiskResizeNotSupportedSubtext",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_NOT_SUPPORTED_SUBTEXT},
      {"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},
      {"crostiniDiskResizeConfirmationDialogTitle",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_CONFIRMATION_DIALOG_TITLE},
      {"crostiniDiskResizeConfirmationDialogMessage",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_CONFIRMATION_DIALOG_MESSAGE},
      {"crostiniDiskResizeConfirmationDialogButton",
       IDS_SETTINGS_CROSTINI_DISK_RESIZE_CONFIRMATION_DIALOG_BUTTON},
      {"crostiniDiskResizeDone", IDS_SETTINGS_CROSTINI_DISK_RESIZE_DONE},
      {"crostiniMicTitle", IDS_SETTINGS_CROSTINI_MIC_TITLE},
      {"crostiniMicDialogLabel", IDS_SETTINGS_CROSTINI_MIC_DIALOG_LABEL},
      {"crostiniMicDialogShutdownButton",
       IDS_SETTINGS_CROSTINI_MIC_DIALOG_SHUTDOWN_BUTTON},
      {"crostiniRemove", IDS_SETTINGS_CROSTINI_REMOVE},
      {"crostiniExtraContainersLabel",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_LABEL},
      {"crostiniExtraContainersDescription",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_DESCRIPTION},
      {"crostiniExtraContainersCreate",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE},
      {"crostiniExtraContainersDelete",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_DELETE},
      {"crostiniExtraContainersStop",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_STOP},
      {"crostiniExtraContainersTableTitle",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_TABLE_TITLE},
      {"crostiniExtraContainersVmNameLabel",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_VM_NAME_LABEL},
      {"crostiniExtraContainersContainerNameLabel",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_NAME_LABEL},
      {"crostiniExtraContainersContainerIpLabel",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL},
      {"crostiniExtraContainersShareMicrophone",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_SHARE_MICROPHONE},
      {"crostiniExtraContainersAppBadgeColor",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_APP_BADGE_COLOR},
      {"crostiniExtraContainersCreateDialogTitle",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_TITLE},
      {"crostiniExtraContainersCreateDialogContainerExistsError",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_CONTAINER_EXISTS_ERROR},
      {"crostiniExtraContainersCreateDialogEmptyContainerNameError",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_EMPTY_CONTAINER_NAME_ERROR},
      {"crostiniExtraContainersCreateDialogImageServer",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_IMAGE_SERVER},
      {"crostiniExtraContainersCreateDialogImageAlias",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_IMAGE_ALIAS},
      {"crostiniExtraContainersCreateDialogAddContainerFile",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_ADD_CONTAINER_LABEL},
      {"crostiniExtraContainersCreateDialogAddContainerButtonLabel",
       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_ADD_CONTAINER_BUTTON_LABEL},
  };
  html_source->AddLocalizedStrings(kLocalizedStrings);

  html_source->AddString(
      "crostiniContainerUpgrade",
      l10n_util::GetStringUTF16(
          IDS_OS_SETTINGS_CROSTINI_CONTAINER_UPGRADE_BOOKWORM_MESSAGE));

  if (auto* pretty_name_value = guest_os::GetContainerPrefValue(
          profile_, crostini::DefaultContainerId(),
          guest_os::prefs::kContainerOsPrettyNameKey)) {
    std::string pretty_name = pretty_name_value->GetString();
    html_source->AddString("crostiniContainerUpgradeSubtext",
                           l10n_util::GetStringFUTF16(
                               IDS_SETTINGS_CROSTINI_CONTAINER_UPGRADE_SUBTEXT,
                               base::UTF8ToUTF16(pretty_name)));
  } else {
    // Blank the subtext if we don't know what the pretty version name is. This
    // is just a fallback for users that haven't opened crostini since before we
    // started recording that.
    html_source->AddString("crostiniContainerUpgradeSubtext", "");
  }

  // Crostini section in settings is always displayed.
  // Should we show that Crostini is supported?
  html_source->AddBoolean(
      "isCrostiniSupported",
      crostini::CrostiniFeatures::Get()->CouldBeAllowed(profile_));

  // Should we actually enable the button to install it?
  html_source->AddBoolean(
      "isCrostiniAllowed",
      crostini::CrostiniFeatures::Get()->IsAllowedNow(profile_));

  // Should Bruschetta be displayed in the settings at all?
  html_source->AddBoolean("showBruschetta", ShouldShowBruschetta(profile_));

  auto bruschetta_name = bruschetta::GetOverallVmName(profile_);

  html_source->AddString("bruschettaPageLabel",
                         l10n_util::GetStringFUTF16(
                             IDS_SETTINGS_BRUSCHETTA_LABEL, bruschetta_name));

  auto learn_more_url =
      base::UTF8ToUTF16(bruschetta::GetLearnMoreUrl(profile_).spec());
  if (learn_more_url.empty()) {
    html_source->AddString(
        "bruschettaSubtext",
        l10n_util::GetStringFUTF16(IDS_SETTINGS_BRUSCHETTA_SUBTEXT_NO_LINK,
                                   ui::GetChromeOSDeviceName()));
  } else {
    html_source->AddString("bruschettaSubtext",
                           l10n_util::GetStringFUTF16(
                               IDS_SETTINGS_BRUSCHETTA_SUBTEXT,
                               ui::GetChromeOSDeviceName(), learn_more_url));
  }

  html_source->AddString(
      "bruschettaSharedUsbDevicesDescription",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_BRUSCHETTA_SHARED_USB_DEVICES_DESCRIPTION,
          bruschetta_name));
  html_source->AddString(
      "bruschettaSharedPathsInstructionsLocate",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_INSTRUCTIONS_LOCATE,
          bruschetta_name,
          base::ASCIIToUTF16(
              bruschetta::BruschettaChromeOSBaseDirectory().value())));
  html_source->AddString(
      "bruschettaSharedPathsInstructionsAdd",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_INSTRUCTIONS_ADD,
          bruschetta_name));
  html_source->AddString(
      "bruschettaSharedPathsRemoveFailureDialogMessage",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_BRUSCHETTA_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE,
          bruschetta_name));
  html_source->AddString("bruschettaRemove",
                         l10n_util::GetStringFUTF16(
                             IDS_SETTINGS_BRUSCHETTA_REMOVE, bruschetta_name));
  html_source->AddString(
      "bruschettaMicTitle",
      l10n_util::GetStringFUTF16(IDS_SETTINGS_BRUSCHETTA_MIC_TITLE,
                                 bruschetta_name));
  html_source->AddString(
      "bruschettaMicDialogLabel",
      l10n_util::GetStringFUTF16(IDS_SETTINGS_BRUSCHETTA_MIC_DIALOG_LABEL,
                                 bruschetta_name));

  html_source->AddString(
      "crostiniSubtext",
      kIsRevampEnabled
          ? l10n_util::GetStringFUTF16(
                IDS_OS_SETTINGS_REVAMP_CROSTINI_SUBTEXT,
                GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL))
          : l10n_util::GetStringFUTF16(
                IDS_SETTINGS_CROSTINI_SUBTEXT, ui::GetChromeOSDeviceName(),
                GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
  html_source->AddString(
      "crostiniSubtextNotSupported",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_CROSTINI_SUBTEXT_NOT_SUPPORTED,
          ui::GetChromeOSDeviceName(),
          GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
  html_source->AddString(
      "crostiniArcAdbPowerwashRequiredSublabel",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_CROSTINI_ARC_ADB_POWERWASH_REQUIRED_SUBLABEL,
          chrome::kArcAdbSideloadingLearnMoreURL));
  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->AddString(
      "crostiniDiskResizeRecommended",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_CROSTINI_DISK_RESIZE_RECOMMENDED,
          ui::FormatBytes(crostini::disk::kRecommendedDiskSizeBytes)));
  html_source->AddString(
      "crostiniDiskResizeRecommendedWarning",
      l10n_util::GetStringFUTF16(
          IDS_SETTINGS_CROSTINI_DISK_RESIZE_RECOMMENDED_WARNING,
          ui::FormatBytes(crostini::disk::kRecommendedDiskSizeBytes)));

  html_source->AddBoolean("showCrostiniExportImport", IsExportImportAllowed());
  html_source->AddBoolean("arcAdbSideloadingSupported",
                          IsAdbSideloadingAllowed());
  html_source->AddBoolean("showCrostiniPortForwarding",
                          IsPortForwardingAllowed());
  html_source->AddBoolean("showCrostiniExtraContainers",
                          IsMultiContainerAllowed());
  html_source->AddBoolean("isOwnerProfile",
                          ProfileHelper::IsOwnerProfile(profile_));
  html_source->AddBoolean("isEnterpriseManaged",
                          IsDeviceManaged() || IsProfileManaged(profile_));
  html_source->AddBoolean("showCrostiniContainerUpgrade",
                          IsContainerUpgradeAllowed());
}

void CrostiniSection::AddHandlers(content::WebUI* web_ui) {
  web_ui->AddMessageHandler(std::make_unique<GuestOsHandler>(profile_));
  web_ui->AddMessageHandler(std::make_unique<CrostiniHandler>(profile_));
}

int CrostiniSection::GetSectionNameMessageId() const {
  return IDS_SETTINGS_CROSTINI_TITLE;
}

mojom::Section CrostiniSection::GetSection() const {
  return ash::features::IsOsSettingsRevampWayfindingEnabled()
             ? mojom::Section::kAboutChromeOs
             : mojom::Section::kCrostini;
}

mojom::SearchResultIcon CrostiniSection::GetSectionIcon() const {
  return mojom::SearchResultIcon::kDeveloperTags;
}

const char* CrostiniSection::GetSectionPath() const {
  return ash::features::IsOsSettingsRevampWayfindingEnabled()
             ? mojom::kAboutChromeOsSectionPath
             : mojom::kCrostiniSectionPath;
}

bool CrostiniSection::LogMetric(mojom::Setting setting,
                                base::Value& value) const {
  // Unimplemented.
  return false;
}

void CrostiniSection::RegisterHierarchy(HierarchyGenerator* generator) const {
  generator->RegisterTopLevelSetting(mojom::Setting::kSetUpCrostini);

  // Crostini details.
  generator->RegisterTopLevelSubpage(IDS_SETTINGS_CROSTINI_LABEL,
                                     mojom::Subpage::kCrostiniDetails,
                                     mojom::SearchResultIcon::kDeveloperTags,
                                     mojom::SearchResultDefaultRank::kMedium,
                                     mojom::kCrostiniDetailsSubpagePath);
  static constexpr mojom::Setting kCrostiniDetailsSettings[] = {
      mojom::Setting::kCrostiniContainerUpgrade,
      mojom::Setting::kCrostiniDiskResize,
      mojom::Setting::kCrostiniMicAccess,
      mojom::Setting::kUninstallCrostini,
      mojom::Setting::kBruschettaMicAccess,
      mojom::Setting::kGuestUsbNotification,
      mojom::Setting::kGuestUsbPersistentPassthrough,
  };
  RegisterNestedSettingBulk(mojom::Subpage::kCrostiniDetails,
                            kCrostiniDetailsSettings, generator);

  // Manage shared folders.
  generator->RegisterNestedSubpage(
      IDS_SETTINGS_GUEST_OS_SHARED_PATHS,
      mojom::Subpage::kCrostiniManageSharedFolders,
      mojom::Subpage::kCrostiniDetails, mojom::SearchResultIcon::kPenguin,
      mojom::SearchResultDefaultRank::kMedium,
      mojom::kCrostiniManageSharedFoldersSubpagePath);

  // USB preferences.
  generator->RegisterNestedSubpage(
      IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_LABEL,
      mojom::Subpage::kCrostiniUsbPreferences, mojom::Subpage::kCrostiniDetails,
      mojom::SearchResultIcon::kPenguin,
      mojom::SearchResultDefaultRank::kMedium,
      mojom::kCrostiniUsbPreferencesSubpagePath);

  // Backup and restore.
  generator->RegisterNestedSubpage(IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE,
                                   mojom::Subpage::kCrostiniBackupAndRestore,
                                   mojom::Subpage::kCrostiniDetails,
                                   mojom::SearchResultIcon::kPenguin,
                                   mojom::SearchResultDefaultRank::kMedium,
                                   mojom::kCrostiniBackupAndRestoreSubpagePath);
  static constexpr mojom::Setting kCrostiniBackupAndRestoreSettings[] = {
      mojom::Setting::kBackupLinuxAppsAndFiles,
      mojom::Setting::kRestoreLinuxAppsAndFiles,
  };
  RegisterNestedSettingBulk(mojom::Subpage::kCrostiniBackupAndRestore,
                            kCrostiniBackupAndRestoreSettings, generator);

  // Develop Android apps.
  generator->RegisterNestedSubpage(
      IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE,
      mojom::Subpage::kCrostiniDevelopAndroidApps,
      mojom::Subpage::kCrostiniDetails, mojom::SearchResultIcon::kDeveloperTags,
      mojom::SearchResultDefaultRank::kMedium,
      mojom::kCrostiniDevelopAndroidAppsSubpagePath);
  generator->RegisterNestedSetting(mojom::Setting::kCrostiniAdbDebugging,
                                   mojom::Subpage::kCrostiniDevelopAndroidApps);

  // Port forwarding.
  generator->RegisterNestedSubpage(IDS_SETTINGS_CROSTINI_PORT_FORWARDING,
                                   mojom::Subpage::kCrostiniPortForwarding,
                                   mojom::Subpage::kCrostiniDetails,
                                   mojom::SearchResultIcon::kPenguin,
                                   mojom::SearchResultDefaultRank::kMedium,
                                   mojom::kCrostiniPortForwardingSubpagePath);

  // Extra containers.
  generator->RegisterNestedSubpage(IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_LABEL,
                                   mojom::Subpage::kCrostiniExtraContainers,
                                   mojom::Subpage::kCrostiniDetails,
                                   mojom::SearchResultIcon::kPenguin,
                                   mojom::SearchResultDefaultRank::kMedium,
                                   mojom::kCrostiniExtraContainersSubpagePath);

  // Bruschetta subpage.
  generator->RegisterTopLevelSubpage(IDS_SETTINGS_BRUSCHETTA_LABEL,
                                     mojom::Subpage::kBruschettaDetails,
                                     mojom::SearchResultIcon::kDeveloperTags,
                                     mojom::SearchResultDefaultRank::kMedium,
                                     mojom::kBruschettaDetailsSubpagePath);

  // USB preferences.
  generator->RegisterNestedSubpage(
      IDS_SETTINGS_GUEST_OS_SHARED_USB_DEVICES_LABEL,
      mojom::Subpage::kBruschettaUsbPreferences,
      mojom::Subpage::kBruschettaDetails, mojom::SearchResultIcon::kPenguin,
      mojom::SearchResultDefaultRank::kMedium,
      mojom::kBruschettaUsbPreferencesSubpagePath);

  // Manage shared folders.
  generator->RegisterNestedSubpage(
      IDS_SETTINGS_GUEST_OS_SHARED_PATHS,
      mojom::Subpage::kBruschettaManageSharedFolders,
      mojom::Subpage::kBruschettaDetails, mojom::SearchResultIcon::kPenguin,
      mojom::SearchResultDefaultRank::kMedium,
      mojom::kBruschettaManageSharedFoldersSubpagePath);
}

bool CrostiniSection::IsExportImportAllowed() const {
  return crostini::CrostiniFeatures::Get()->IsExportImportUIAllowed(profile_);
}

bool CrostiniSection::IsContainerUpgradeAllowed() const {
  return crostini::ShouldAllowContainerUpgrade(profile_);
}

bool CrostiniSection::IsPortForwardingAllowed() const {
  return crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_);
}

bool CrostiniSection::IsMultiContainerAllowed() const {
  return crostini::CrostiniFeatures::Get()->IsMultiContainerAllowed(profile_);
}

void CrostiniSection::UpdateSearchTags() {
  SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();

  updater.RemoveSearchTags(GetCrostiniOptedInSearchConcepts());
  updater.RemoveSearchTags(
      GetCrostiniOptedOutSearchConcepts(GetSection(), GetSectionPath()));
  updater.RemoveSearchTags(GetCrostiniExportImportSearchConcepts());
  updater.RemoveSearchTags(GetCrostiniAdbSideloadingSearchConcepts());
  updater.RemoveSearchTags(GetCrostiniPortForwardingSearchConcepts());
  updater.RemoveSearchTags(GetCrostiniContainerUpgradeSearchConcepts());
  updater.RemoveSearchTags(GetCrostiniDiskResizingSearchConcepts());

  if (!crostini::CrostiniFeatures::Get()->IsAllowedNow(profile_) ||
      !pref_service_->GetBoolean(crostini::prefs::kCrostiniEnabled)) {
    updater.AddSearchTags(
        GetCrostiniOptedOutSearchConcepts(GetSection(), GetSectionPath()));
    return;
  }

  updater.AddSearchTags(GetCrostiniOptedInSearchConcepts());

  if (IsExportImportAllowed()) {
    updater.AddSearchTags(GetCrostiniExportImportSearchConcepts());
  }

  if (IsAdbSideloadingAllowed() &&
      pref_service_->GetBoolean(arc::prefs::kArcEnabled)) {
    updater.AddSearchTags(GetCrostiniAdbSideloadingSearchConcepts());
  }

  if (IsPortForwardingAllowed()) {
    updater.AddSearchTags(GetCrostiniPortForwardingSearchConcepts());
  }

  if (IsContainerUpgradeAllowed()) {
    updater.AddSearchTags(GetCrostiniContainerUpgradeSearchConcepts());
  }

  updater.AddSearchTags(GetCrostiniDiskResizingSearchConcepts());

  // TODO(crbug:1261319): search concepts for extras containers.
}

}  // namespace ash::settings