chromium/ash/webui/personalization_app/personalization_app_ui.cc

// Copyright 2021 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 "ash/webui/personalization_app/personalization_app_ui.h"

#include <memory>
#include <string>

#include "ash/constants/ash_features.h"
#include "ash/public/cpp/ambient/ambient_backend_controller.h"
#include "ash/public/cpp/ambient/ambient_client.h"
#include "ash/rgb_keyboard/rgb_keyboard_manager.h"
#include "ash/shell.h"
#include "ash/wallpaper/wallpaper_constants.h"
#include "ash/webui/common/mojom/sea_pen.mojom.h"
#include "ash/webui/common/sea_pen_provider.h"
#include "ash/webui/common/sea_pen_resources.h"
#include "ash/webui/common/sea_pen_resources_generated.h"
#include "ash/webui/common/trusted_types_util.h"
#include "ash/webui/grit/ash_personalization_app_resources.h"
#include "ash/webui/grit/ash_personalization_app_resources_map.h"
#include "ash/webui/personalization_app/personalization_app_ambient_provider.h"
#include "ash/webui/personalization_app/personalization_app_keyboard_backlight_provider.h"
#include "ash/webui/personalization_app/personalization_app_theme_provider.h"
#include "ash/webui/personalization_app/personalization_app_user_provider.h"
#include "ash/webui/personalization_app/personalization_app_wallpaper_provider.h"
#include "base/check.h"
#include "base/containers/contains.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/crosapi/cpp/lacros_startup_state.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "components/manta/features.h"
#include "content/public/browser/browser_context.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 "services/network/public/mojom/content_security_policy.mojom-shared.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/devicetype_utils.h"
#include "ui/resources/grit/webui_resources.h"
#include "ui/webui/color_change_listener/color_change_handler.h"
#include "ui/webui/mojo_web_ui_controller.h"

namespace ash::personalization_app {

namespace {

std::u16string GetGooglePhotosURL() {
  return u"https://photos.google.com";
}

bool IsAmbientModeAllowed() {
  return ash::AmbientClient::Get() &&
         ash::AmbientClient::Get()->IsAmbientModeAllowed();
}

AmbientBackendController* GetAmbientBackendController() {
  AmbientBackendController* ambient_backend_controller =
      AmbientBackendController::Get();
  DCHECK(ambient_backend_controller);
  return ambient_backend_controller;
}

void AddResources(content::WebUIDataSource* source) {
  source->AddResourcePath("", IDR_ASH_PERSONALIZATION_APP_INDEX_HTML);
  source->AddResourcePaths(base::make_span(
      kAshPersonalizationAppResources, kAshPersonalizationAppResourcesSize));
  source->AddResourcePath("test_loader.html", IDR_WEBUI_TEST_LOADER_HTML);
  source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER_JS);
  source->AddResourcePath("test_loader_util.js",
                          IDR_WEBUI_JS_TEST_LOADER_UTIL_JS);

#if !DCHECK_IS_ON()
  // Add a default path to avoid crashes when not debugging.
  source->SetDefaultResource(IDR_ASH_PERSONALIZATION_APP_INDEX_HTML);
#endif  // !DCHECK_IS_ON()
}

void AddStrings(content::WebUIDataSource* source) {
  static constexpr webui::LocalizedString kLocalizedStrings[] = {
      {"personalizationTitle",
       IDS_PERSONALIZATION_APP_PERSONALIZATION_HUB_TITLE},
      {"wallpaperLabel", IDS_PERSONALIZATION_APP_WALLPAPER_LABEL},
      {"defaultWallpaper", IDS_PERSONALIZATION_APP_DEFAULT_WALLPAPER},
      {"back", IDS_PERSONALIZATION_APP_BACK_BUTTON},
      {"currentlySet", IDS_PERSONALIZATION_APP_CURRENTLY_SET},
      {"descriptionDialogOpen",
       IDS_PERSONALIZATION_APP_WALLPAPER_DESCRIPTION_DIALOG_OPEN},
      {"descriptionDialogClose",
       IDS_PERSONALIZATION_APP_WALLPAPER_DESCRIPTION_DIALOG_CLOSE},
      {"myImagesLabel", IDS_PERSONALIZATION_APP_MY_IMAGES},
      {"wallpaperCollections", IDS_PERSONALIZATION_APP_WALLPAPER_COLLECTIONS},
      {"center", IDS_PERSONALIZATION_APP_CENTER},
      {"fill", IDS_PERSONALIZATION_APP_FILL},
      {"changeDaily", IDS_PERSONALIZATION_APP_CHANGE_DAILY},
      {"ariaLabelChangeDaily", IDS_PERSONALIZATION_APP_ARIA_LABEL_CHANGE_DAILY},
      {"refresh", IDS_PERSONALIZATION_APP_REFRESH},
      {"ariaLabelRefresh", IDS_PERSONALIZATION_APP_ARIA_LABEL_REFRESH},
      {"dailyRefresh", IDS_PERSONALIZATION_APP_DAILY_REFRESH},
      {"unknownImageAttribution",
       IDS_PERSONALIZATION_APP_UNKNOWN_IMAGE_ATTRIBUTION},
      {"wallpaperNetworkError",
       IDS_PERSONALIZATION_APP_WALLPAPER_NETWORK_ERROR},
      {"ariaLabelLoading", IDS_PERSONALIZATION_APP_ARIA_LABEL_LOADING},
      // Using old wallpaper app error string pending final revision.
      // TODO(b/195609442)
      {"setWallpaperError", IDS_PERSONALIZATION_APP_SET_WALLPAPER_ERROR},
      {"loadWallpaperError", IDS_PERSONALIZATION_APP_LOAD_WALLPAPER_ERROR},
      {"dismiss", IDS_PERSONALIZATION_APP_DISMISS},
      {"ariaLabelViewFullScreen",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_VIEW_FULL_SCREEN},
      {"exitFullscreen", IDS_PERSONALIZATION_APP_EXIT_FULL_SCREEN},
      {"ariaLabelExitFullscreen",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_EXIT_FULL_SCREEN},
      {"setAsWallpaper", IDS_PERSONALIZATION_APP_SET_AS_WALLPAPER},
      {"zeroImages", IDS_PERSONALIZATION_APP_NO_IMAGES},
      {"oneImage", IDS_PERSONALIZATION_APP_ONE_IMAGE},
      {"multipleImages", IDS_PERSONALIZATION_APP_MULTIPLE_IMAGES},
      {"managedFeature", IDS_PERSONALIZATION_APP_MANAGED_FEATURE},
      {"managedSetting", IDS_PERSONALIZATION_APP_MANAGED_SETTING},
      {"ariaLabelChangeWallpaper",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_CHANGE_WALLPAPER},
      {"ariaLabelHome", IDS_PERSONALIZATION_APP_ARIA_LABEL_HOME},

      // Theme related strings.
      {"themeLabel", IDS_PERSONALIZATION_APP_THEME_LABEL},
      {"dynamicColorLabel", IDS_PERSONALIZATION_APP_THEME_DYNAMIC_COLOR_LABEL},
      {"dynamicColorDescription",
       IDS_PERSONALIZATION_APP_THEME_DYNAMIC_COLOR_DESCRIPTION},
      {"colorSchemeTonalSpot",
       IDS_PERSONALIZATION_APP_THEME_COLOR_SCHEME_TONAL_SPOT},
      {"colorSchemeNeutral",
       IDS_PERSONALIZATION_APP_THEME_COLOR_SCHEME_NEUTRAL},
      {"colorSchemeVibrant",
       IDS_PERSONALIZATION_APP_THEME_COLOR_SCHEME_VIBRANT},
      {"colorSchemeExpressive",
       IDS_PERSONALIZATION_APP_THEME_COLOR_SCHEME_EXPRESSIVE},
      {"staticColorGoogleBlue",
       IDS_PERSONALIZATION_APP_THEME_STATIC_COLOR_GOOGLE_BLUE},
      {"staticColorLightPink",
       IDS_PERSONALIZATION_APP_THEME_STATIC_COLOR_LIGHT_PINK},
      {"staticColorDarkGreen",
       IDS_PERSONALIZATION_APP_THEME_STATIC_COLOR_DARK_GREEN},
      {"staticColorLightPurple",
       IDS_PERSONALIZATION_APP_THEME_STATIC_COLOR_LIGHT_PURPLE},
      {"darkColorMode", IDS_PERSONALIZATION_APP_THEME_DARK_COLOR_MODE},
      {"lightColorMode", IDS_PERSONALIZATION_APP_THEME_LIGHT_COLOR_MODE},
      {"autoColorMode", IDS_PERSONALIZATION_APP_THEME_AUTO_COLOR_MODE},
      {"ariaLabelEnableDarkColorMode",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_ENABLE_DARK_COLOR_MODE},
      {"ariaLabelEnableLightColorMode",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_ENABLE_LIGHT_COLOR_MODE},
      {"ariaLabelEnableAutoColorMode",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_ENABLE_AUTO_COLOR_MODE},
      {"tooltipAutoColorMode", IDS_PERSONALIZATION_APP_TOOLTIP_AUTO_COLOR_MODE},
      {"errorTooltipAutoColorMode",
       IDS_PERSONALIZATION_APP_ERROR_TOOLTIP_AUTO_COLOR_MODE},
      {"geolocationWarningTextForWeather",
       IDS_PERSONALIZATION_APP_THEME_GEOLOCATION_WARNING_TEXT_FOR_WEATHER},
      {"autoModeGeolocationDialogText",
       IDS_PERSONALIZATION_APP_THEME_GEOLOCATION_DIALOG_BODY},
      {"autoModeGeolocationDialogConfirmButton",
       IDS_PERSONALIZATION_APP_THEME_GEOLOCATION_DIALOG_CONFIRM_BUTTON},
      {"autoModeGeolocationDialogCancelButton",
       IDS_PERSONALIZATION_APP_THEME_GEOLOCATION_DIALOG_CANCEL_BUTTON},
      {"systemGeolocationDialogTitle",
       IDS_PERSONALIZATION_APP_GEOLOCATION_DIALOG_TITLE},
      {"systemGeolocationDialogBodyParagraph1",
       IDS_PERSONALIZATION_APP_GEOLOCATION_DIALOG_BODY_PARAGRAPH1},
      {"systemGeolocationDialogBodyParagraph2",
       IDS_PERSONALIZATION_APP_GEOLOCATION_DIALOG_BODY_PARAGRAPH2},
      {"systemGeolocationDialogConfirmButton",
       IDS_PERSONALIZATION_APP_GEOLOCATION_DIALOG_CONFIRM_BUTTON},
      {"systemGeolocationDialogCancelButton",
       IDS_PERSONALIZATION_APP_GEOLOCATION_DIALOG_CANCEL_BUTTON},

      // User/avatar related strings.
      {"avatarLabel", IDS_PERSONALIZATION_APP_AVATAR_LABEL},
      {"takeWebcamPhoto", IDS_PERSONALIZATION_APP_AVATAR_TAKE_PHOTO},
      {"takeWebcamVideo", IDS_PERSONALIZATION_APP_AVATAR_TAKE_VIDEO},
      {"webcamCaptureInProgress",
       IDS_PERSONALIZATION_APP_AVATAR_CAPTURE_IN_PROGRESS},
      {"confirmWebcamPhoto", IDS_PERSONALIZATION_APP_AVATAR_CONFIRM_PHOTO},
      {"confirmWebcamVideo", IDS_PERSONALIZATION_APP_AVATAR_CONFIRM_VIDEO},
      {"rejectWebcamPhoto", IDS_PERSONALIZATION_APP_AVATAR_REJECT_PHOTO},
      {"ariaLabelChangeAvatar",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_CHANGE_AVATAR},
      {"ariaLabelGoToAccountSettings",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_GO_TO_ACCOUNT_SETTINGS},
      {"googleProfilePhoto",
       IDS_PERSONALIZATION_APP_AVATAR_GOOGLE_PROFILE_PHOTO},
      {"chooseAFile", IDS_PERSONALIZATION_APP_AVATAR_CHOOSE_A_FILE},
      {"lastExternalImageTitle",
       IDS_PERSONALIZATION_APP_AVATAR_LAST_EXTERNAL_IMAGE},
      {"ariaLabelCurrentAvatar",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_CURRENT_AVATAR},
      {"ariaAnnounceAvatarChanged",
       IDS_PERSONALIZATION_APP_ARIA_ANNOUNCE_AVATAR_CHANGED},
      {"ariaLabelCloseCamera",
       IDS_PERSONALIZATION_APP_AVATAR_ARIA_LABEL_CLOSE_CAMERA},
      {"ariaLabelWebcamVideo",
       IDS_PERSONALIZATION_APP_AVATAR_ARIA_LABEL_WEBCAM_VIDEO},
      {"avatarNetworkError", IDS_PERSONALIZATION_APP_AVATAR_NETWORK_ERROR},

      // Ambient mode related string.
      {"screensaverLabel", IDS_PERSONALIZATION_APP_SCREENSAVER_LABEL},
      {"screenSaverPreviewButton",
       IDS_PERSONALIZATION_APP_SCREENSAVER_PREVIEW_BUTTON},
      {"screenSaverPreviewButtonAriaLabel",
       IDS_PERSONALIZATION_APP_SCREENSAVER_PREVIEW_BUTTON_ARIA_LABEL},
      {"screenSaverPreviewDownloading",
       IDS_PERSONALIZATION_APP_SCREENSAVER_PREVIEW_DOWNLOADING},
      {"screenSaverPreviewDownloadingAriaLabel",
       IDS_PERSONALIZATION_APP_SCREENSAVER_PREVIEW_DOWNLOADING_ARIA_LABEL},
      {"ambientModePageDescription",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_PAGE_DESCRIPTION},
      {"ambientModeOn", IDS_PERSONALIZATION_APP_AMBIENT_MODE_ON},
      {"ambientModeOff", IDS_PERSONALIZATION_APP_AMBIENT_MODE_OFF},
      {"ambientModeDurationTitle",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_DURATION_TITLE},
      {"ambientModeDurationMinutes",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_DURATION_MINUTES},
      {"ambientModeDurationOneHour",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_DURATION_ONE_HOUR},
      {"ambientModeDurationForever",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_DURATION_FOREVER},
      {"ambientModeTopicSourceTitle",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_TITLE},
      {"ambientModeTopicSourceGooglePhotos",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS},
      {"ambientModeTopicSourceGooglePhotosDescription",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESC},
      {"ambientModeTopicSourceGooglePhotosDescriptionNoAlbum",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESC_NO_ALBUM},
      {"ambientModeTopicSourceArtGallery",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY},
      {"ambientModeTopicSourceArtGalleryDescription",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY_DESCRIPTION},
      {"ambientModeTopicSourceVideo",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_VIDEO},
      {"ambientModeTopicSourceSelectedRow",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_SELECTED_ROW},
      {"ambientModeTopicSourceUnselectedRow",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW},
      {"ambientModeWeatherTitle",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_TITLE},
      {"ambientModeAriaDescriptionWeather",
       IDS_PERSONALIZATION_APP_ARIA_DESCRIPTION_AMBIENT_MODE_WEATHER},
      {"ambientModeWeatherUnitFahrenheit",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_UNIT_FAHRENHEIT},
      {"ambientModeWeatherUnitCelsius",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_WEATHER_UNIT_CELSIUS},
      {"ambientModeAlbumsSubpageRecentHighlightsDesc",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_RECENT_DESC},
      {"ambientModeAlbumsSubpagePhotosNumPluralDesc",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM_PLURAL},
      {"ambientModeAlbumsSubpagePhotosNumSingularDesc",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM_SINGULAR},
      {"ambientModeAlbumsSubpageAlbumSelected",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_ALBUM_SELECTED},
      {"ambientModeAlbumsSubpageAlbumUnselected",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_ALBUM_UNSELECTED},
      {"ambientModeLastArtAlbumMessage",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_LAST_ART_ALBUM_MESSAGE},
      {"ambientModeArtAlbumDialogCloseButtonLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ART_ALBUM_DIALOG_CLOSE_BUTTON_LABEL},
      {"ambientModeAnimationTitle",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_TITLE},
      {"ambientModeAnimationSlideshowLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_SLIDESHOW_LABEL},
      {"ambientModeAnimationFeelTheBreezeLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_FEEL_THE_BREEZE_LABEL},
      {"ambientModeAnimationFloatOnByLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_FLOAT_ON_BY_LABEL},
      {"ambientModeAnimationVideoLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ANIMATION_VIDEO_LABEL},
      {"ambientModeZeroStateMessage",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_ZERO_STATE_MESSAGE},
      {"ambientModeMultipleAlbumsDesc",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_MULTIPLE_ALBUMS_DESC},
      {"ambientModeMainPageZeroStateMessage",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE},
      {"ambientModeMainPageZeroStateMessageV2",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ZERO_STATE_MESSAGE_V2},
      {"ambientModeMainPageEnterpriseUserMessage",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_MAIN_PAGE_ENTERPRISE_USER_MESSAGE},
      {"ambientModeTurnOnLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_TURN_ON_LABEL},
      {"ambientModeLearnMoreLabel",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_LEARN_MORE_LABEL},
      {"ariaLabelChangeScreensaver",
       IDS_PERSONALIZATION_APP_ARIA_LABEL_CHANGE_SCREENSAVER},
      {"ambientModeNetworkError",
       IDS_PERSONALIZATION_APP_AMBIENT_MODE_NETWORK_ERROR},

      // Keyboard backlight strings
      {"keyboardBacklightTitle",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_TITLE},
      {"wallpaperColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_LABEL},
      {"wallpaperColorTooltipText",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_TOOLTIP_TEXT},
      {"whiteColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WHITE_COLOR_LABEL},
      {"redColor", IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_RED_COLOR_LABEL},
      {"yellowColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_YELLOW_COLOR_LABEL},
      {"greenColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_GREEN_COLOR_LABEL},
      {"blueColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_BLUE_COLOR_LABEL},
      {"indigoColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_INDIGO_COLOR_LABEL},
      {"purpleColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_PURPLE_COLOR_LABEL},
      {"rainbowColor",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_RAINBOW_COLOR_LABEL},
      {"wallpaperColorNudgeText",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_NUDGE_TEXT},
      {"zoneCustomize",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_CUSTOMIZATION_BUTTON},
      {"dismissButtonText",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_CUSTOMIZATION_DISMISS_BUTTON},
      {"wallpaperColorDescription",
       IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_DESCRIPTION},
      {"zoneTitle", IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_TITLE},
      {"keyboardZonesTitle", IDS_PERSONALIZATION_APP_KEYBOARD_ZONES_TITLE},

      // Google Photos strings
      // TODO(b/229149314): Finalize error and retry strings.
      {"googlePhotosLabel", IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS},
      {"googlePhotosError", IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_ERROR},
      {"googlePhotosTryAgain", IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_TRY_AGAIN},
      {"googlePhotosAlbumShared",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_ALBUM_SHARED},
      {"googlePhotosSharedAlbumDialogTitle",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_SHARED_ALBUM_DIALOG_TITLE},
      {"googlePhotosSharedAlbumDialogContent",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_SHARED_ALBUM_DIALOG_CONTENT},
      {"googlePhotosSharedAlbumDialogCloseButton",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_SHARED_ALBUM_DIALOG_CLOSE_BUTTON},
      {"googlePhotosSharedAlbumDialogAcceptButton",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_SHARED_ALBUM_DIALOG_ACCEPT_BUTTON},
      {"googlePhotosAlbumsTabLabel",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_ALBUMS_TAB},
      {"googlePhotosPhotosTabLabel",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_PHOTOS_TAB},
      {"googlePhotosZeroStateMessage",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_ZERO_STATE_MESSAGE},
      {"googlePhotosAlbumZeroStateMessage",
       IDS_PERSONALIZATION_APP_GOOGLE_PHOTOS_ALBUM_ZERO_STATE_MESSAGE},

      // Time of day Wallpaper/Screen saver strings
      {"timeOfDayBannerDescription",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_BANNER_DESCRIPTION},
      {"timeOfDayBannerDescriptionNoScreensaver",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_BANNER_DESCRIPTION_NO_SCREENSAVER},
      {"timeOfDayWallpaperCollectionSublabel",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_COLLECTION_SUBLABEL},
      {"timeOfDayWallpaperDialogTitle",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_TITLE},
      {"timeOfDayWallpaperDialogContent",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONTENT},
      {"timeOfDayWallpaperDialogBackButton",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_BACK_BUTTON},
      {"timeOfDayWallpaperDialogConfirmButton",
       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONFIRM_BUTTON}};

  source->AddLocalizedStrings(kLocalizedStrings);

  ::ash::common::AddSeaPenStrings(source);
  ::ash::common::AddSeaPenWallpaperTemplateStrings(source);

  source->AddString("googlePhotosURL", GetGooglePhotosURL());

  source->AddString("timeOfDayWallpaperCollectionId",
                    wallpaper_constants::kTimeOfDayWallpaperCollectionId);

  source->AddString("timeOfDayBannerImageUrl",
                    GetAmbientBackendController()->GetPromoBannerUrl());

  source->AddString("geolocationAccuracyLearnMoreUrl",
                    kPrivacyHubGeolocationAccuracyLearnMoreURL);

  // Product name does not need to be translated.
  auto product_name =
      l10n_util::GetStringUTF16(ui::GetChromeOSDeviceTypeResourceId());
  if (features::IsTimeOfDayScreenSaverEnabled()) {
    product_name = base::UTF8ToUTF16(
        GetAmbientBackendController()->GetTimeOfDayProductName());
  }
  source->AddString(
      "ambientModeTopicSourceVideoDescription",
      l10n_util::GetStringFUTF16(
          IDS_PERSONALIZATION_APP_AMBIENT_MODE_TOPIC_SOURCE_VIDEO_DESCRIPTION,
          product_name));
  source->AddString(
      "timeOfDayBannerTitle",
      l10n_util::GetStringFUTF16(
          IDS_PERSONALIZATION_APP_TIME_OF_DAY_BANNER_TITLE, product_name));

  source->AddString(
      "ambientModeAlbumsSubpageGooglePhotosTitle",
      l10n_util::GetStringFUTF16(
          IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_TITLE,
          GetGooglePhotosURL()));
  source->AddString(
      "ambientModeAlbumsSubpageGooglePhotosNoAlbum",
      l10n_util::GetStringFUTF16(
          IDS_PERSONALIZATION_APP_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_NO_ALBUM,
          GetGooglePhotosURL()));

  source->UseStringsJs();
  source->EnableReplaceI18nInJS();
}

bool ShouldHandleWebUIRequest(const std::string& path) {
  return base::StartsWith(path, "wallpaper.jpg");
}

}  // namespace

PersonalizationAppUI::PersonalizationAppUI(
    content::WebUI* web_ui,
    std::unique_ptr<PersonalizationAppAmbientProvider> ambient_provider,
    std::unique_ptr<PersonalizationAppKeyboardBacklightProvider>
        keyboard_backlight_provider,
    std::unique_ptr<::ash::common::SeaPenProvider> sea_pen_provider,
    std::unique_ptr<PersonalizationAppThemeProvider> theme_provider,
    std::unique_ptr<PersonalizationAppUserProvider> user_provider,
    std::unique_ptr<PersonalizationAppWallpaperProvider> wallpaper_provider)
    : ui::MojoWebUIController(web_ui),
      ambient_provider_(std::move(ambient_provider)),
      keyboard_backlight_provider_(std::move(keyboard_backlight_provider)),
      sea_pen_provider_(std::move(sea_pen_provider)),
      theme_provider_(std::move(theme_provider)),
      user_provider_(std::move(user_provider)),
      wallpaper_provider_(std::move(wallpaper_provider)) {
  start_time_ = base::Time::Now();
  DCHECK(wallpaper_provider_);

  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      web_ui->GetWebContents()->GetBrowserContext(),
      kChromeUIPersonalizationAppHost);

  // Supply a custom wallpaper image.
  source->SetRequestFilter(
      base::BindRepeating(&ShouldHandleWebUIRequest),
      base::BindRepeating(&PersonalizationAppUI::HandleWebUIRequest,
                          weak_ptr_factory_.GetWeakPtr()));

  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ScriptSrc,
      "script-src chrome://resources chrome://webui-test 'self';");

  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::WorkerSrc,
      "worker-src blob: chrome://resources 'self';");

  ash::EnableTrustedTypesCSP(source);

  AddResources(source);
  AddStrings(source);
  AddBooleans(source);
  AddIntegers(source);
}

PersonalizationAppUI::~PersonalizationAppUI() {
  base::TimeDelta duration = base::Time::Now() - start_time_;
  base::UmaHistogramCustomTimes("Ash.Personalization.App.Duration", duration,
                                /*min=*/base::Minutes(1),
                                /*max=*/base::Minutes(30),
                                /*buckets=*/31);
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<personalization_app::mojom::AmbientProvider>
        receiver) {
  ambient_provider_->BindInterface(std::move(receiver));
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<personalization_app::mojom::KeyboardBacklightProvider>
        receiver) {
  keyboard_backlight_provider_->BindInterface(std::move(receiver));
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<::ash::personalization_app::mojom::SeaPenProvider>
        receiver) {
  sea_pen_provider_->BindInterface(std::move(receiver));
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<personalization_app::mojom::ThemeProvider> receiver) {
  theme_provider_->BindInterface(std::move(receiver));
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<personalization_app::mojom::WallpaperProvider>
        receiver) {
  wallpaper_provider_->BindInterface(std::move(receiver));
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<personalization_app::mojom::UserProvider> receiver) {
  user_provider_->BindInterface(std::move(receiver));
}

void PersonalizationAppUI::BindInterface(
    mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
  color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
      web_ui()->GetWebContents(), std::move(receiver));
}

void PersonalizationAppUI::AddBooleans(content::WebUIDataSource* source) {
  source->AddBoolean("isGooglePhotosIntegrationEnabled",
                     wallpaper_provider_->IsEligibleForGooglePhotos());

  source->AddBoolean("isGooglePhotosSharedAlbumsEnabled",
                     features::IsWallpaperGooglePhotosSharedAlbumsEnabled());

  source->AddBoolean("isAmbientModeAllowed", IsAmbientModeAllowed());

  source->AddBoolean(
      "isRgbKeyboardSupported",
      Shell::Get()->rgb_keyboard_manager()->IsRgbKeyboardSupported());

  source->AddBoolean("isUserAvatarCustomizationSelectorsEnabled",
                     user_provider_->IsCustomizationSelectorsPrefEnabled());

  source->AddBoolean("isTimeOfDayScreenSaverEnabled",
                     features::IsTimeOfDayScreenSaverEnabled());

  source->AddBoolean("isTimeOfDayWallpaperEnabled",
                     features::IsTimeOfDayWallpaperEnabled());

  source->AddBoolean("isCrosPrivacyHubLocationEnabled",
                     features::IsCrosPrivacyHubLocationEnabled());

  const bool common_sea_pen_requirements =
      sea_pen_provider_->IsEligibleForSeaPen() &&
      manta::features::IsMantaServiceEnabled();
  source->AddBoolean("isSeaPenEnabled",
                     ::ash::features::IsSeaPenEnabled() &&
                         common_sea_pen_requirements);
  source->AddBoolean("isSeaPenTextInputEnabled",
                     common_sea_pen_requirements &&
                         ::ash::features::IsSeaPenTextInputEnabled() &&
                         sea_pen_provider_->IsEligibleForSeaPenTextInput());
  source->AddBoolean("isSeaPenUseExptTemplateEnabled",
                     common_sea_pen_requirements &&
                         ::ash::features::IsSeaPenUseExptTemplateEnabled());
  source->AddBoolean("isManagedSeaPenEnabled",
                     sea_pen_provider_->IsManagedSeaPenEnabled());
  source->AddBoolean("isManagedSeaPenFeedbackEnabled",
                     sea_pen_provider_->IsManagedSeaPenFeedbackEnabled());
  source->AddBoolean("isLacrosEnabled",
                     ::crosapi::lacros_startup_state::IsLacrosEnabled());
  source->AddBoolean("isVcResizeThumbnailEnabled", false);
}

void PersonalizationAppUI::AddIntegers(content::WebUIDataSource* source) {
  source->AddInteger("keyboardBacklightZoneCount",
                     features::IsMultiZoneRgbKeyboardEnabled()
                         ? Shell::Get()->rgb_keyboard_manager()->GetZoneCount()
                         : 0);
}

void PersonalizationAppUI::HandleWebUIRequest(
    const std::string& path,
    content::WebUIDataSource::GotDataCallback callback) {
  DCHECK(base::Contains(path, "?key="))
      << "wallpaper key must be provided to prevent browser cache collisions";
  wallpaper_provider_->GetWallpaperAsJpegBytes(std::move(callback));
}

WEB_UI_CONTROLLER_TYPE_IMPL(PersonalizationAppUI)

}  // namespace ash::personalization_app