// Copyright 2012 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/extensions/extensions_ui.h"

#include <memory>
#include <string>
#include <utility>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/permissions_url_constants.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/managed_ui_handler.h"
#include "chrome/browser/ui/webui/metrics_handler.h"
#include "chrome/browser/ui/webui/page_not_available_for_guest/page_not_available_for_guest_ui.h"
#include "chrome/browser/ui/webui/plural_string_handler.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/extensions_resources.h"
#include "chrome/grit/extensions_resources_map.h"
#include "chrome/grit/generated_resources.h"
#include "components/favicon_base/favicon_url_parser.h"
#include "components/google/core/common/google_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/manifest_v2_handler.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/extension_urls.h"
#include "extensions/grit/extensions_browser_resources.h"
#include "extensions/strings/grit/extensions_strings.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/webui/webui_util.h"

#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/webui/current_channel_logo.h"
#endif  // BUILDFLAG(IS_ANDROID)

namespace extensions {

namespace {

constexpr char kInDevModeKey[] = "inDevMode";
constexpr char kShowActivityLogKey[] = "showActivityLog";
constexpr char kLoadTimeClassesKey[] = "loadTimeClasses";
constexpr char kEnableEnhancedSiteControls[] = "enableEnhancedSiteControls";

std::string GetLoadTimeClasses(bool in_dev_mode) {
  return in_dev_mode ? "in-dev-mode" : std::string();
}

bool IsGlobalShortcutEnabled() {
// Disable the global scoped shortcuts on Android and ChromeOS since they're
// no-ops.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
  return false;
#else
  return true;
#endif
}

content::WebUIDataSource* CreateAndAddExtensionsSource(Profile* profile,
                                                       bool in_dev_mode) {
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      profile, chrome::kChromeUIExtensionsHost);
  webui::SetupWebUIDataSource(source, kExtensionsResources,
                              IDR_EXTENSIONS_EXTENSIONS_HTML);

  static constexpr webui::LocalizedString kLocalizedStrings[] = {
      // Add common strings.
      {"add", IDS_ADD},
      {"back", IDS_ACCNAME_BACK},
      {"cancel", IDS_CANCEL},
      {"close", IDS_CLOSE},
      {"clear", IDS_CLEAR},
      {"confirm", IDS_CONFIRM},
      {"controlledSettingChildRestriction",
       IDS_CONTROLLED_SETTING_CHILD_RESTRICTION},
      {"controlledSettingPolicy", IDS_CONTROLLED_SETTING_POLICY},
      {"done", IDS_DONE},
      {"learnMore", IDS_LEARN_MORE},
      {"menu", IDS_MENU},
      {"noSearchResults", IDS_SEARCH_NO_RESULTS},
      {"ok", IDS_OK},
      {"save", IDS_SAVE},
      {"searchResultsPlural", IDS_SEARCH_RESULTS_PLURAL},
      {"searchResultsSingular", IDS_SEARCH_RESULTS_SINGULAR},

      // Multi-use strings defined in extensions_strings.grdp.
      {"remove", IDS_EXTENSIONS_REMOVE},
      {"moreOptions", IDS_EXTENSIONS_MORE_OPTIONS},

      // Add extension-specific strings.
      {"title", IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE},
      {"toolbarTitle", IDS_EXTENSIONS_TOOLBAR_TITLE},
      {"mainMenu", IDS_EXTENSIONS_MENU_BUTTON_LABEL},
      {"search", IDS_EXTENSIONS_SEARCH},
      {"clearSearch", IDS_CLEAR_SEARCH},
      {"sidebarExtensions", IDS_EXTENSIONS_SIDEBAR_EXTENSIONS},
      {"appsTitle", IDS_EXTENSIONS_APPS_TITLE},
      {"noExtensionsOrApps", IDS_EXTENSIONS_NO_INSTALLED_ITEMS},
      {"noDescription", IDS_EXTENSIONS_NO_DESCRIPTION},
      {"viewInStore", IDS_EXTENSIONS_ITEM_CHROME_WEB_STORE},
      {"extensionWebsite", IDS_EXTENSIONS_ITEM_EXTENSION_WEBSITE},
      {"dropToInstall", IDS_EXTENSIONS_INSTALL_DROP_TARGET},
      {"editSitePermissionsAllowAllExtensions",
       IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_ALLOW_ALL_EXTENSIONS},
      {"editSitePermissionsCustomizePerExtension",
       IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_CUSTOMIZE_PER_EXTENSION},
      {"editSitePermissionsRestrictExtensions",
       IDS_EXTENSIONS_EDIT_SITE_PERMISSIONS_RESTRICT_EXTENSIONS},
      {"errorsPageHeading", IDS_EXTENSIONS_ERROR_PAGE_HEADING},
      {"clearActivities", IDS_EXTENSIONS_CLEAR_ACTIVITIES},
      {"clearAll", IDS_EXTENSIONS_ERROR_CLEAR_ALL},
      {"clearEntry", IDS_EXTENSIONS_A11Y_CLEAR_ENTRY},
      {"logLevel", IDS_EXTENSIONS_LOG_LEVEL_INFO},
      {"warnLevel", IDS_EXTENSIONS_LOG_LEVEL_WARN},
      {"errorLevel", IDS_EXTENSIONS_LOG_LEVEL_ERROR},
      {"anonymousFunction", IDS_EXTENSIONS_ERROR_ANONYMOUS_FUNCTION},
      {"errorContext", IDS_EXTENSIONS_ERROR_CONTEXT},
      {"errorContextUnknown", IDS_EXTENSIONS_ERROR_CONTEXT_UNKNOWN},
      {"safetyCheckExtensionsDetailPagePrimaryLabel",
       IDS_EXTENSIONS_SAFETY_CHECK_PRIMARY_LABEL},
      {"safetyCheckExtensionsKeep", IDS_CONFIRM_DOWNLOAD},
      {"stackTrace", IDS_EXTENSIONS_ERROR_STACK_TRACE},
      {"sidebarDiscoverMore", IDS_EXTENSIONS_SIDEBAR_DISCOVER_MORE},
      {"sidebarDocsPromo", IDS_EXTENSIONS_MODERN_WEB_GUIDANCE_PROMO},
      {"keyboardShortcuts", IDS_EXTENSIONS_SIDEBAR_KEYBOARD_SHORTCUTS},
      {"incognitoInfoWarning", IDS_EXTENSIONS_INCOGNITO_WARNING},
      {"userScriptInfoWarning", IDS_EXTENSIONS_ALLOW_USER_SCRIPTS_WARNING},
      {"hostPermissionsDescription",
       IDS_EXTENSIONS_HOST_PERMISSIONS_DESCRIPTION},
      {"permissionsLearnMoreLabel",
       IDS_EXTENSIONS_PERMISSIONS_LEARN_MORE_A11Y_LABEL},
      {"hostPermissionsEdit", IDS_EXTENSIONS_HOST_PERMISSIONS_EDIT},
      {"hostPermissionsHeading", IDS_EXTENSIONS_ITEM_HOST_PERMISSIONS_HEADING},
      {"newHostPermissionsHeading",
       IDS_EXTENSIONS_NEW_HOST_PERMISSIONS_HEADING},
      {"hostPermissionsSubHeading",
       IDS_EXTENSIONS_HOST_PERMISSIONS_SUB_HEADING},
      {"hostAccessAskOnEveryVisit",
       IDS_EXTENSIONS_HOST_ACCESS_ASK_ON_EVERY_VISIT},
      {"hostAccessOnClick", IDS_EXTENSIONS_HOST_ACCESS_ON_CLICK},
      {"hostAccessOnSpecificSites",
       IDS_EXTENSIONS_HOST_ACCESS_ON_SPECIFIC_SITES},
      {"hostAccessAllowOnSpecificSites",
       IDS_EXTENSIONS_HOST_ACCESS_ALLOW_ON_SPECIFIC_SITES},
      {"hostAccessOnAllSites", IDS_EXTENSIONS_HOST_ACCESS_ON_ALL_SITES},
      {"hostAccessAllowOnAllSites",
       IDS_EXTENSIONS_HOST_ACCESS_ALLOW_ON_ALL_SITES},
      {"hostAllowedHosts", IDS_EXTENSIONS_ITEM_ALLOWED_HOSTS},
      {"itemId", IDS_EXTENSIONS_ITEM_ID},
      {"itemInspectViews", IDS_EXTENSIONS_ITEM_INSPECT_VIEWS},
      // NOTE: This text reads "<n> more". It's possible that it should be using
      // a plural string instead. Unfortunately, this is non-trivial since we
      // don't expose that capability to JS yet. Since we don't know it's a
      // problem, use a simple placeholder for now.
      {"itemInspectViewsExtra", IDS_EXTENSIONS_ITEM_INSPECT_VIEWS_EXTRA},
      {"noActiveViews", IDS_EXTENSIONS_ITEM_NO_ACTIVE_VIEWS},
      {"itemAllowIncognito", IDS_EXTENSIONS_ITEM_ALLOW_INCOGNITO},
      {"itemAllowUserScripts", IDS_EXTENSIONS_ITEM_ALLOW_USER_SCRIPTS},
      {"itemDescriptionLabel", IDS_EXTENSIONS_ITEM_DESCRIPTION},
      {"itemDependencies", IDS_EXTENSIONS_ITEM_DEPENDENCIES},
      {"itemDependentEntry", IDS_EXTENSIONS_DEPENDENT_ENTRY},
      {"itemDetails", IDS_EXTENSIONS_ITEM_DETAILS},
      {"itemRateExtension", IDS_EXTENSIONS_ITEM_RATE_IT},
      {"itemDetailsBackButtonAriaLabel",
       IDS_EXTENSIONS_DETAILS_BACK_BUTTON_ARIA_LABEL},
      {"itemDetailsBackButtonRoleDescription",
       IDS_EXTENSIONS_DETAILS_BACK_BUTTON_ARIA_ROLE_DESCRIPTION},
      {"itemErrors", IDS_EXTENSIONS_ITEM_ERRORS},
      {"itemWarnings", IDS_EXTENSIONS_ITEM_WARNINGS},
      {"accessibilityErrorLine", IDS_EXTENSIONS_ACCESSIBILITY_ERROR_LINE},
      {"accessibilityErrorMultiLine",
       IDS_EXTENSIONS_ACCESSIBILITY_ERROR_MULTI_LINE},
      {"activityLogPageHeading", IDS_EXTENSIONS_ACTIVITY_LOG_PAGE_HEADING},
      {"activityLogTypeColumn", IDS_EXTENSIONS_ACTIVITY_LOG_TYPE_COLUMN},
      {"activityLogNameColumn", IDS_EXTENSIONS_ACTIVITY_LOG_NAME_COLUMN},
      {"activityLogCountColumn", IDS_EXTENSIONS_ACTIVITY_LOG_COUNT_COLUMN},
      {"activityLogTimeColumn", IDS_EXTENSIONS_ACTIVITY_LOG_TIME_COLUMN},
      {"activityLogSearchLabel", IDS_EXTENSIONS_ACTIVITY_LOG_SEARCH_LABEL},
      {"activityLogHistoryTabHeading",
       IDS_EXTENSIONS_ACTIVITY_LOG_HISTORY_TAB_HEADING},
      {"activityLogStreamTabHeading",
       IDS_EXTENSIONS_ACTIVITY_LOG_STREAM_TAB_HEADING},
      {"startActivityStream", IDS_EXTENSIONS_START_ACTIVITY_STREAM},
      {"stopActivityStream", IDS_EXTENSIONS_STOP_ACTIVITY_STREAM},
      {"parentDisabledPermissions", IDS_EXTENSIONS_PERMISSIONS_OFF},
      {"emptyStreamStarted", IDS_EXTENSIONS_EMPTY_STREAM_STARTED},
      {"emptyStreamStopped", IDS_EXTENSIONS_EMPTY_STREAM_STOPPED},
      {"activityArgumentsHeading", IDS_EXTENSIONS_ACTIVITY_ARGUMENTS_HEADING},
      {"webRequestInfoHeading", IDS_EXTENSIONS_WEB_REQUEST_INFO_HEADING},
      {"activityLogMoreActionsLabel",
       IDS_EXTENSIONS_ACTIVITY_LOG_MORE_ACTIONS_LABEL},
      {"activityLogExpandAll", IDS_EXTENSIONS_ACTIVITY_LOG_EXPAND_ALL},
      {"activityLogCollapseAll", IDS_EXTENSIONS_ACTIVITY_LOG_COLLAPSE_ALL},
      {"activityLogExportHistory", IDS_EXTENSIONS_ACTIVITY_LOG_EXPORT_HISTORY},
      {"appIcon", IDS_EXTENSIONS_APP_ICON},
      {"extensionIcon", IDS_EXTENSIONS_EXTENSION_ICON},
      {"extensionA11yAssociation", IDS_EXTENSIONS_EXTENSION_A11Y_ASSOCIATION},
      {"extensionsSectionHeader", IDS_EXTENSIONS_SECTION_HEADER},
      {"pinNewExtensions", IDS_EXTENSIONS_PIN_NEW_EXTENSIONS},
      {"itemIdHeading", IDS_EXTENSIONS_ITEM_ID_HEADING},
      {"extensionEnabled", IDS_EXTENSIONS_EXTENSION_ENABLED},
      {"appEnabled", IDS_EXTENSIONS_APP_ENABLED},
      {"installWarnings", IDS_EXTENSIONS_INSTALL_WARNINGS},
      {"itemExtensionPath", IDS_EXTENSIONS_PATH},
      {"itemOff", IDS_EXTENSIONS_ITEM_OFF},
      {"itemOn", IDS_EXTENSIONS_ITEM_ON},
      {"itemOptions", IDS_EXTENSIONS_ITEM_OPTIONS},
      {"itemPermissions", IDS_EXTENSIONS_ITEM_PERMISSIONS},
      {"itemPermissionsEmpty", IDS_EXTENSIONS_ITEM_PERMISSIONS_EMPTY},
      {"itemPermissionsAndSiteAccessEmpty",
       IDS_EXTENSIONS_ITEM_PERMISSIONS_AND_SITE_ACCESS_EMPTY},
      {"itemPinToToolbar", IDS_EXTENSIONS_ITEM_PIN_TO_TOOLBAR},
      {"itemRemoveExtension", IDS_EXTENSIONS_ITEM_REMOVE_EXTENSION},
      {"itemShowAccessRequestsInToolbar",
       IDS_EXTENSIONS_ITEM_SHOW_ACCESS_REQUESTS_IN_TOOLBAR},
      {"itemShowAccessRequestsLearnMore",
       IDS_EXTENSIONS_ACCESS_REQUESTS_LEARN_MORE},
      {"itemSiteAccess", IDS_EXTENSIONS_ITEM_SITE_ACCESS},
      {"itemSiteAccessAddHost", IDS_EXTENSIONS_ITEM_SITE_ACCESS_ADD_HOST},
      {"itemSiteAccessEmpty", IDS_EXTENSIONS_ITEM_SITE_ACCESS_EMPTY},
      {"itemSource", IDS_EXTENSIONS_ITEM_SOURCE},
      {"itemSourceInstalledByDefault",
       IDS_EXTENSIONS_ITEM_SOURCE_INSTALLED_BY_DEFAULT},
      {"itemSourcePolicy", IDS_EXTENSIONS_ITEM_SOURCE_POLICY},
      {"itemSourceSideloaded", IDS_EXTENSIONS_ITEM_SOURCE_SIDELOADED},
      {"itemSourceUnpacked", IDS_EXTENSIONS_ITEM_SOURCE_UNPACKED},
      {"itemSourceWebstore", IDS_EXTENSIONS_ITEM_SOURCE_WEBSTORE},
      {"itemVersion", IDS_EXTENSIONS_ITEM_VERSION},
      {"itemReloaded", IDS_EXTENSIONS_ITEM_RELOADED},
      {"itemReloading", IDS_EXTENSIONS_ITEM_RELOADING},
      // TODO(dpapad): Replace this with an Extensions specific string.
      {"itemSize", IDS_DIRECTORY_LISTING_SIZE},
      {"itemAllowOnFileUrls", IDS_EXTENSIONS_ALLOW_FILE_ACCESS},
      {"itemAllowOnAllSites", IDS_EXTENSIONS_ALLOW_ON_ALL_URLS},
      {"itemAllowOnFollowingSites", IDS_EXTENSIONS_ALLOW_ON_FOLLOWING_SITES},
      {"itemCollectErrors", IDS_EXTENSIONS_ENABLE_ERROR_COLLECTION},
      {"itemCorruptInstall", IDS_EXTENSIONS_CORRUPTED_EXTENSION},
      {"itemAllowlistWarning",
       IDS_EXTENSIONS_SAFE_BROWSING_CRX_ALLOWLIST_WARNING},
      {"itemAllowlistWarningLearnMoreLabel",
       IDS_EXTENSIONS_SAFE_BROWSING_CRX_ALLOWLIST_WARNING_LEARN_MORE},
      {"itemRepair", IDS_EXTENSIONS_REPAIR_CORRUPTED},
      {"itemReload", IDS_EXTENSIONS_RELOAD_TERMINATED},
      {"itemUpload", IDS_EXTENSIONS_MOVE_TO_ACCOUNT_ICON_TOOLTIP},
      {"itemUnsupportedDeveloperMode",
       IDS_EXTENSIONS_DISABLED_UNSUPPORTED_DEVELOPER_MODE},
      {"itemUnsupportedDeveloperModeDetails",
       IDS_EXTENSIONS_DISABLED_UNSUPPORTED_DEVELOPER_MODE_DETAILS},
      {"itemUnsupportedDeveloperModeToast",
       IDS_EXTENSIONS_DISABLED_UNSUPPORTED_DEVELOPER_MODE_TOAST},
      {"loadErrorCouldNotLoadManifest",
       IDS_EXTENSIONS_LOAD_ERROR_COULD_NOT_LOAD_MANIFEST},
      {"loadErrorHeading", IDS_EXTENSIONS_LOAD_ERROR_HEADING},
      {"loadErrorFileLabel", IDS_EXTENSIONS_LOAD_ERROR_FILE_LABEL},
      {"loadErrorErrorLabel", IDS_EXTENSIONS_LOAD_ERROR_ERROR_LABEL},
      {"loadErrorRetry", IDS_EXTENSIONS_LOAD_ERROR_RETRY},
      {"loadingActivities", IDS_EXTENSIONS_LOADING_ACTIVITIES},
      {"matchingRestrictedSitesAllow",
       IDS_EXTENSIONS_MATCHING_RESTRICTED_SITES_ALLOW},
      {"matchingRestrictedSitesTitle",
       IDS_EXTENSIONS_MATCHING_RESTRICTED_SITES_TITLE},
      {"matchingRestrictedSitesWarning",
       IDS_EXTENSIONS_MATCHING_RESTRICTED_SITES_WARNING},
      {"missingOrUninstalledExtension", IDS_MISSING_OR_UNINSTALLED_EXTENSION},
      {"noActivities", IDS_EXTENSIONS_NO_ACTIVITIES},
      {"noErrorsToShow", IDS_EXTENSIONS_ERROR_NO_ERRORS_CODE_MESSAGE},
      {"opensInNewTab", IDS_EXTENSIONS_OPENS_IN_NEW_TAB},
      {"removeSitesDialogTitle", IDS_EXTENSIONS_REMOVE_SITES_DIALOG_TITLE},
      {"runtimeHostsDialogInputError",
       IDS_EXTENSIONS_RUNTIME_HOSTS_DIALOG_INPUT_ERROR},
      {"runtimeHostsDialogInputLabel",
       IDS_EXTENSIONS_RUNTIME_HOSTS_DIALOG_INPUT_LABEL},
      {"runtimeHostsDialogTitle", IDS_EXTENSIONS_RUNTIME_HOSTS_DIALOG_TITLE},
      {"packDialogTitle", IDS_EXTENSIONS_PACK_DIALOG_TITLE},
      {"packDialogWarningTitle", IDS_EXTENSIONS_PACK_DIALOG_WARNING_TITLE},
      {"packDialogErrorTitle", IDS_EXTENSIONS_PACK_DIALOG_ERROR_TITLE},
      {"packDialogProceedAnyway", IDS_EXTENSIONS_PACK_DIALOG_PROCEED_ANYWAY},
      {"packDialogBrowse", IDS_EXTENSIONS_PACK_DIALOG_BROWSE_BUTTON},
      {"packDialogExtensionRoot",
       IDS_EXTENSIONS_PACK_DIALOG_EXTENSION_ROOT_LABEL},
      {"packDialogKeyFile", IDS_EXTENSIONS_PACK_DIALOG_KEY_FILE_LABEL},
      {"packDialogContent", IDS_EXTENSION_PACK_DIALOG_HEADING},
      {"packDialogConfirm", IDS_EXTENSIONS_PACK_DIALOG_CONFIRM_BUTTON},
      {"publishedInStoreRequiredByPolicy",
       IDS_EXTENSIONS_DISABLED_PUBLISHED_IN_STORE_REQUIRED_BY_POLICY},
      {"sitePermissions", IDS_EXTENSIONS_SITE_PERMISSIONS},
      {"sitePermissionsAllSitesPageTitle",
       IDS_EXTENSIONS_SITE_PERMISSIONS_ALL_SITES_PAGE_TITLE},
      {"sitePermissionsAllSitesExtensionCount",
       // TODO(crbug.com/40251278): Convert the below two strings to use plural
       // strings.
       IDS_EXTENSIONS_SITE_PERMISSIONS_ALL_SITES_EXTENSION_COUNT},
      {"sitePermissionsAllSitesOneExtension",
       IDS_EXTENSIONS_SITE_PERMISSIONS_ALL_SITES_ONE_EXTENSION},
      {"sitePermissionsAlwaysOnAllSites",
       IDS_EXTENSIONS_SITE_PERMISSIONS_ALWAYS_ON_ALL_SITES},
      {"sitePermissionsAlwaysOnThisSite",
       IDS_EXTENSIONS_SITE_PERMISSIONS_ALWAYS_ON_THIS_SITE},
      {"sitePermissionsAskOnEveryVisit",
       IDS_EXTENSIONS_SITE_PERMISSIONS_ASK_ON_EVERY_VISIT},
      {"sitePermissionsPageTitle", IDS_EXTENSIONS_SITE_PERMISSIONS_PAGE_TITLE},
      {"sitePermissionsAddSiteDialogTitle",
       IDS_EXTENSIONS_SITE_PERMISSIONS_ADD_SITE_DIALOG_TITLE},
      {"sitePermissionsEditSiteDialogTitle",
       IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_SITE_DIALOG_TITLE},
      {"sitePermissionsDialogInputError",
       IDS_EXTENSIONS_SITE_PERMISSIONS_DIALOG_INPUT_ERROR},
      {"sitePermissionsDialogInputLabel",
       IDS_EXTENSIONS_SITE_PERMISSIONS_DIALOG_INPUT_LABEL},
      {"sitePermissionsEditPermissions",
       IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS},
      {"sitePermissionsEditPermissionsDialogTitle",
       IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_PERMISSIONS_DIALOG_TITLE},
      {"sitePermissionsEditUrl", IDS_EXTENSIONS_SITE_PERMISSIONS_EDIT_URL},
      {"sitePermissionsIncludesSubdomains",
       IDS_EXTENSIONS_SITE_PERMISSIONS_INCLUDES_SUBDOMAINS},
      {"sitePermissionsViewAllSites",
       IDS_EXTENSIONS_SITE_PERMISSIONS_VIEW_ALL_SITES},
      {"siteSettings", IDS_EXTENSIONS_SITE_SETTINGS},
      {"permittedSites", IDS_EXTENSIONS_PERMITTED_SITES},
      {"restrictedSites", IDS_EXTENSIONS_RESTRICTED_SITES},
      {"noSitesAdded", IDS_EXTENSIONS_NO_SITES_ADDED},
      {"editShortcutInputLabel", IDS_EXTENSIONS_EDIT_SHORTCUT_INPUT_LABEL},
      {"editShortcutButtonLabel", IDS_EXTENSIONS_EDIT_SHORTCUT_BUTTON_LABEL},
      {"mv2DeprecationPanelTitle", IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_TITLE},
      {"mv2DeprecationPanelDismissButton",
       IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_DISMISS_BUTTON},
      {"mv2DeprecationPanelExtensionActionMenuLabel",
       IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_ACTION_MENU_BUTTON_LABEL},
      {"mv2DeprecationPanelFindAlternativeButton",
       IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_FIND_ALTERNATIVE_BUTTON},
      {"mv2DeprecationPanelFindAlternativeButtonAccLabel",
       IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_FIND_ALTERNATIVE_BUTTON_ACC_LABEL},
      {"mv2DeprecationPanelRemoveButtonAccLabel",
       IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_REMOVE_BUTTON_ACC_LABEL},
      {"mv2DeprecationMessageDisabledHeader",
       IDS_EXTENSIONS_MV2_DEPRECATION_MESSAGE_DISABLED_HEADER},
      {"mv2DeprecationMessageDisabledSubtitle",
       IDS_EXTENSIONS_MV2_DEPRECATION_MESSAGE_DISABLED_SUBTITLE},
      {"mv2DeprecationMessageRemoveButton",
       IDS_EXTENSIONS_MV2_DEPRECATION_MESSAGE_REMOVE_BUTTON},
      {"mv2DeprecationMessageWarningHeader",
       IDS_EXTENSIONS_MV2_DEPRECATION_MESSAGE_WARNING_HEADER},
      {"mv2DeprecationMessageWarningSubtitle",
       IDS_EXTENSIONS_MV2_DEPRECATION_MESSAGE_WARNING_SUBTITLE},
      {"mv2DeprecationUnsupportedExtensionOffText",
       IDS_EXTENSIONS_MV2_DEPRECATION_UNSUPPORTED_EXTENSION_OFF_TEXT},
      {"setShortcutInSystemSettings",
       IDS_EXTENSIONS_SET_SHORTCUT_IN_SYSTEM_SETTINGS},
      {"shortcutNotSet", IDS_SHORTCUT_NOT_SET},
      {"shortcutInputLabel", IDS_EXTENSIONS_SHORTCUT_INPUT_LABEL},
      {"shortcutScope", IDS_EXTENSIONS_SHORTCUT_SCOPE},
      {"shortcutClear", IDS_SHORTCUT_CLEAR},
      {"shortcutScopeGlobal", IDS_EXTENSIONS_SHORTCUT_SCOPE_GLOBAL},
      {"shortcutScopeLabel", IDS_EXTENSIONS_SHORTCUT_SCOPE_LABEL},
      {"shortcutScopeInChrome", IDS_EXTENSIONS_SHORTCUT_SCOPE_IN_CHROME},
      {"shortcutSet", IDS_SHORTCUT_SET},
      {"shortcutTypeAShortcut", IDS_TYPE_A_SHORTCUT},
      {"shortcutIncludeStartModifier", IDS_SHORTCUT_INCLUDE_START_MODIFIER},
      {"shortcutTooManyModifiers", IDS_SHORTCUT_TOO_MANY_MODIFIERS},
      {"shortcutNeedCharacter", IDS_SHORTCUT_NEED_CHARACTER},
      {"subpageArrowRoleDescription", IDS_EXTENSIONS_SUBPAGE_BUTTON},
      {"itemSuspiciousInstallLearnMore",
       IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE_LEARN_MORE},
      {"toolbarDevMode", IDS_EXTENSIONS_DEVELOPER_MODE},
      {"toolbarLoadUnpacked", IDS_EXTENSIONS_TOOLBAR_LOAD_UNPACKED},
      {"toolbarLoadUnpackedDone", IDS_EXTENSIONS_TOOLBAR_LOAD_UNPACKED_DONE},
      {"toolbarPack", IDS_EXTENSIONS_TOOLBAR_PACK},
      {"toolbarUpdateNow", IDS_EXTENSIONS_TOOLBAR_UPDATE_NOW},
      {"toolbarUpdateNowTooltip", IDS_EXTENSIONS_TOOLBAR_UPDATE_NOW_TOOLTIP},
      {"toolbarUpdateDone", IDS_EXTENSIONS_TOOLBAR_UPDATE_DONE},
      {"toolbarUpdatingToast", IDS_EXTENSIONS_TOOLBAR_UPDATING_TOAST},
      {"updateRequiredByPolicy",
       IDS_EXTENSIONS_DISABLED_UPDATE_REQUIRED_BY_POLICY},
      {"viewActivityLog", IDS_EXTENSIONS_VIEW_ACTIVITY_LOG},
      {"viewBackgroundPage", IDS_EXTENSIONS_BACKGROUND_PAGE},
      {"viewIncognito", IDS_EXTENSIONS_VIEW_INCOGNITO},
      {"viewInDevTools", IDS_EXTENSIONS_VIEW_IN_DEVTOOLS},
      {"viewInactive", IDS_EXTENSIONS_VIEW_INACTIVE},
      {"viewIframe", IDS_EXTENSIONS_VIEW_IFRAME},
      {"viewServiceWorker", IDS_EXTENSIONS_SERVICE_WORKER_BACKGROUND},
      {"safetyCheckKeepExtension", IDS_EXTENSIONS_SC_KEEP_EXT},
      {"safetyCheckExtensionThreeDotDetails",
       IDS_EXTENSIONS_SC_THREEDOT_DETAILS},
      {"safetyCheckRemoveAll", IDS_EXTENSIONS_SC_REMOVE_ALL},
      {"safetyHubHeader", IDS_EXTENSIONS_SAFETY_HUB_HEADER},
      {"safetyCheckRemoveButtonA11yLabel",
       IDS_EXTENSIONS_SC_REMOVE_BUTTON_A11Y_LABEL},
      {"safetyCheckOptionMenuA11yLabel",
       IDS_EXTENSIONS_SC_OPTION_MENU_A11Y_LABEL},
#if BUILDFLAG(IS_CHROMEOS)
      {"manageKioskApp", IDS_EXTENSIONS_MANAGE_KIOSK_APP},
      {"kioskAddApp", IDS_EXTENSIONS_KIOSK_ADD_APP},
      {"kioskAddAppHint", IDS_EXTENSIONS_KIOSK_ADD_APP_HINT},
      {"kioskEnableAutoLaunch", IDS_EXTENSIONS_KIOSK_ENABLE_AUTO_LAUNCH},
      {"kioskDisableAutoLaunch", IDS_EXTENSIONS_KIOSK_DISABLE_AUTO_LAUNCH},
      {"kioskAutoLaunch", IDS_EXTENSIONS_KIOSK_AUTO_LAUNCH},
      {"kioskInvalidApp", IDS_EXTENSIONS_KIOSK_INVALID_APP},
      {"kioskDisableBailout",
       IDS_EXTENSIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_LABEL},
      {"kioskDisableBailoutWarningTitle",
       IDS_EXTENSIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_TITLE},
#endif  // BUILDFLAG(IS_CHROMEOS)
      {"pendingChangeWarning", IDS_PENDING_CHANGE_WARNING},
  };
  source->AddLocalizedStrings(kLocalizedStrings);

  // Add localized generic strings that need '&' to be removed from them.
  webui::AddLocalizedString(source, "edit", IDS_EDIT);

  source->AddString("errorLinesNotShownSingular",
                    l10n_util::GetPluralStringFUTF16(
                        IDS_EXTENSIONS_ERROR_LINES_NOT_SHOWN, 1));
  source->AddString("errorLinesNotShownPlural",
                    l10n_util::GetPluralStringFUTF16(
                        IDS_EXTENSIONS_ERROR_LINES_NOT_SHOWN, 2));
  source->AddString(
      "itemSuspiciousInstall",
      l10n_util::GetStringFUTF16(
          IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
          l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
  source->AddString(
      "suspiciousInstallHelpUrl",
      base::ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
                             GURL(chrome::kRemoveNonCWSExtensionURL),
                             g_browser_process->GetApplicationLocale())
                             .spec()));
  source->AddString("enhancedSafeBrowsingWarningHelpUrl",
                    chrome::kCwsEnhancedSafeBrowsingLearnMoreURL);
  source->AddString(
      "getMoreExtensionsUrl",
      base::ASCIIToUTF16(
          google_util::AppendGoogleLocaleParam(
              extension_urls::AppendUtmSource(
                  extension_urls::GetWebstoreExtensionsCategoryURL(),
                  extension_urls::kExtensionsSidebarUtmSource),
              g_browser_process->GetApplicationLocale())
              .spec()));
  source->AddString(
      "modernWebGuidanceURL",
      base::ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
                             extension_urls::AppendUtmSource(
                                 extension_urls::GetModernWebGuidanceURL(),
                                 extension_urls::kExtensionsSidebarUtmSource),
                             g_browser_process->GetApplicationLocale())
                             .spec()));
  source->AddString(
      "hostPermissionsLearnMoreLink",
      extension_permissions_constants::kRuntimeHostPermissionsHelpURL);
  source->AddBoolean(kInDevModeKey, in_dev_mode);
  source->AddBoolean(
      "enableExtensionsPinnedByDefault",
      base::FeatureList::IsEnabled(features::kExtensionsPinnedByDefault));
  // `prefs::kExtensionReviewPromptsAllowed` is a dynamic preference that can
  // change at runtime. We evaluate it statically at page load as a tradeoff:
  // stale values will be updated the next time the page loads, and backend
  // checks prevent execution if disabled mid-session.
  source->AddBoolean("cwsReviewPromptingEnabled",
                     base::FeatureList::IsEnabled(
                         extensions_features::kCWSReviewPromptingNativeUI) &&
                         profile->IsRegularProfile() &&
                         profile->GetPrefs()->GetBoolean(
                             prefs::kExtensionReviewPromptsAllowed));
  source->AddBoolean(kShowActivityLogKey,
                     base::CommandLine::ForCurrentProcess()->HasSwitch(
                         ::switches::kEnableExtensionActivityLogging));

  source->AddString(kLoadTimeClassesKey, GetLoadTimeClasses(in_dev_mode));

  source->AddBoolean(kEnableEnhancedSiteControls,
                     base::FeatureList::IsEnabled(
                         extensions_features::kExtensionsMenuAccessControl));
  source->AddString(
      "showAccessRequestsInToolbarLearnMoreLink",
      extension_permissions_constants::kShowAccessRequestsInToolbarHelpURL);
  source->AddBoolean(
      "enableUserPermittedSites",
      base::FeatureList::IsEnabled(
          extensions_features::kExtensionsMenuAccessControlWithPermittedSites));
  source->AddBoolean(
      "safetyHubThreeDotDetails",
      base::FeatureList::IsEnabled(features::kSafetyHubThreeDotDetails));
  source->AddBoolean("enableGlobalScopedShortcuts", IsGlobalShortcutEnabled());

  // MV2 deprecation.
  auto* mv2_handler = ManifestV2Handler::Get(profile);
  source->AddBoolean("MV2DeprecationNoticeDismissed",
                     mv2_handler->DidUserAcknowledgeNoticeGlobally());

#if BUILDFLAG(IS_ANDROID)
  source->AddResourcePath("images/product_logo.png",
                          webui::CurrentChannelLogoResourceId());
#endif  // BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(IS_CHROMEOS)
  source->AddString(
      "kioskDisableBailoutWarningBody",
      l10n_util::GetStringFUTF16(
          IDS_EXTENSIONS_KIOSK_DISABLE_BAILOUT_SHORTCUT_WARNING_BODY,
          l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_OS_NAME)));
#endif  // BUILDFLAG(IS_CHROMEOS)

  source->AddString("webuiRefresh2026", features::IsWebuiRefresh2026Enabled()
                                            ? "webui-refresh-2026"
                                            : "");

  return source;
}

}  // namespace

ExtensionsUIConfig::ExtensionsUIConfig()
    : WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIExtensionsHost) {}

ExtensionsUIConfig::~ExtensionsUIConfig() = default;

std::unique_ptr<content::WebUIController>
ExtensionsUIConfig::CreateWebUIController(content::WebUI* web_ui,
                                          const GURL& url) {
  Profile* profile = Profile::FromWebUI(web_ui);
  if (profile->IsGuestSession()) {
    return std::make_unique<PageNotAvailableForGuestUI>(
        web_ui, chrome::kChromeUIExtensionsHost);
  }
  return std::make_unique<ExtensionsUI>(web_ui);
}

ExtensionsUI::ExtensionsUI(content::WebUI* web_ui)
    : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true) {
  Profile* profile = Profile::FromWebUI(web_ui);
  content::WebUIDataSource* source = nullptr;

  in_dev_mode_.Init(prefs::kExtensionsUIDeveloperMode, profile->GetPrefs(),
                    base::BindRepeating(&ExtensionsUI::OnDevModeChanged,
                                        base::Unretained(this)));

  source = CreateAndAddExtensionsSource(profile, *in_dev_mode_);
  ManagedUIHandler::Initialize(web_ui, source);

  // Need to allow <object> elements so that the <extensionoptions> browser
  // plugin can be loaded within chrome://extensions.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ObjectSrc, "object-src 'self';");

  content::URLDataSource::Add(
      profile, std::make_unique<FaviconSource>(
                   profile, chrome::FaviconUrlFormat::kFavicon2));

  content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));

  // Add a handler to provide pluralized strings.
  auto plural_string_handler = std::make_unique<PluralStringHandler>();
  plural_string_handler->AddLocalizedString("safetyCheckTitle",
                                            IDS_EXTENSIONS_SC_TITLE);
  plural_string_handler->AddLocalizedString("safetyCheckDescription",
                                            IDS_EXTENSIONS_SC_DESCRIPTION);
  plural_string_handler->AddLocalizedString("safetyCheckAllDoneForNow",
                                            IDS_EXTENSIONS_SC_ALL_DONE_FOR_NOW);
  plural_string_handler->AddLocalizedString(
      "mv2DeprecationPanelWarningHeader",
      IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_WARNING_HEADER);
  plural_string_handler->AddLocalizedString(
      "mv2DeprecationPanelWarningSubtitle",
      IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_WARNING_SUBTITLE);
  plural_string_handler->AddLocalizedString(
      "mv2DeprecationPanelDisabledHeader",
      IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_DISABLED_HEADER);
  plural_string_handler->AddLocalizedString(
      "mv2DeprecationPanelDisabledSubtitle",
      IDS_EXTENSIONS_MV2_DEPRECATION_PANEL_DISABLED_SUBTITLE);
  web_ui->AddMessageHandler(std::move(plural_string_handler));
}

ExtensionsUI::~ExtensionsUI() = default;

// static
scoped_refptr<base::RefCountedMemory> ExtensionsUI::GetFaviconResourceBytes(
    ui::ResourceScaleFactor scale_factor) {
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  return rb.LoadDataResourceBytesForScale(IDR_EXTENSIONS_FAVICON, scale_factor);
}

// Normally volatile data does not belong in loadTimeData, but in this case
// prevents flickering on a very prominent surface (top of the landing page).
void ExtensionsUI::OnDevModeChanged() {
  base::DictValue update;
  update.Set(kInDevModeKey, *in_dev_mode_);
  update.Set(kLoadTimeClassesKey, GetLoadTimeClasses(*in_dev_mode_));
  content::WebUIDataSource::Update(Profile::FromWebUI(web_ui()),
                                   chrome::kChromeUIExtensionsHost,
                                   std::move(update));
}

}  // namespace extensions
