chromium/ash/webui/shortcut_customization_ui/shortcut_customization_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/shortcut_customization_ui/shortcut_customization_app_ui.h"

#include <memory>
#include <utility>

#include "ash/constants/ash_features.h"
#include "ash/shell.h"
#include "ash/webui/common/backend/shortcut_input_provider.h"
#include "ash/webui/common/trusted_types_util.h"
#include "ash/webui/grit/ash_shortcut_customization_app_resources.h"
#include "ash/webui/grit/ash_shortcut_customization_app_resources_map.h"
#include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h"
#include "ash/webui/shortcut_customization_ui/backend/search/search.mojom.h"
#include "ash/webui/shortcut_customization_ui/backend/search/search_handler.h"
#include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h"
#include "ash/webui/shortcut_customization_ui/shortcuts_app_manager.h"
#include "ash/webui/shortcut_customization_ui/shortcuts_app_manager_factory.h"
#include "ash/webui/shortcut_customization_ui/url_constants.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chromeos/strings/grit/chromeos_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 "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/ui_base_features.h"
#include "ui/resources/grit/webui_resources.h"
#include "ui/views/widget/widget.h"
#include "ui/webui/color_change_listener/color_change_handler.h"
#include "ui/webui/mojo_web_ui_controller.h"

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "chromeos/ash/resources/internal/strings/grit/ash_internal_strings.h"
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

namespace ash {

namespace {

void SetUpWebUIDataSource(content::WebUIDataSource* source,
                          base::span<const webui::ResourcePath> resources,
                          int default_resource) {
  source->AddResourcePaths(resources);
  source->SetDefaultResource(default_resource);
  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);
}

void AddLocalizedStrings(content::WebUIDataSource* source) {
  static constexpr webui::LocalizedString kLocalizedStrings[] = {
      {"appTitle", IDS_SHORTCUT_CUSTOMIZATION_APP_TITLE},
      {"keyboardSettings", IDS_SHORTCUT_CUSTOMIZATION_KEYBOARD_SETTINGS},
      {"addShortcut", IDS_SHORTCUT_CUSTOMIZATION_ADD_SHORTCUT},
      {"restoreDefaults", IDS_SHORTCUT_CUSTOMIZATION_RESTORE_DEFAULTS},
      {"editable", IDS_SHORTCUT_CUSTOMIZATION_ROW_STATUS_EDITABLE},
      {"editButtonForRow", IDS_SHORTCUT_CUSTOMIZATION_EDIT_BUTTON_FOR_ROW},
      {"editButtonForAction",
       IDS_SHORTCUT_CUSTOMIZATION_EDIT_BUTTON_FOR_ACTION},
      {"deleteButtonForAction",
       IDS_SHORTCUT_CUSTOMIZATION_DELETE_BUTTON_FOR_ACTION},
      {"editShortcut", IDS_SHORTCUT_CUSTOMIZATION_EDIT_SHORTCUT},
      {"editDialogDone", IDS_SHORTCUT_CUSTOMIZATION_EDIT_DIALOG_DONE},
      {"lock", IDS_SHORTCUT_CUSTOMIZATION_LOCK},
      {"locked", IDS_SHORTCUT_CUSTOMIZATION_ROW_STATUS_LOCKED},
      {"cancel", IDS_SHORTCUT_CUSTOMIZATION_CANCEL},
      {"editViewStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_EDIT_VIEW_STATUS_MESSAGE},
      {"restoreDefaultConflictMessage",
       IDS_SHORTCUT_CUSTOMIZATION_RESTORE_DEFAULT_ERROR_MESSAGE},
      {"resetAllShortcuts", IDS_SHORTCUT_CUSTOMIZATION_RESET_ALL_SHORTCUTS},
      {"confirmResetAllShortcutsTitle",
       IDS_SHORTCUT_CUSTOMIZATION_CONFIRM_RESET_ALL_SHORTCUTS_TITLE},
      {"confirmResetAllShortcutsButton",
       IDS_SHORTCUT_CUSTOMIZATION_CONFIRM_RESET_SHORTCUTS_BUTTON},
      {"customizationDisabledByPolicyMessage",
       IDS_SHORTCUT_CUSTOMIZATION_CUSTOMIZATION_DISABLED_BY_POLICY_MESSAGE},
      {"categoryGeneral", IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_GENERAL},
      {"categoryDevice", IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_DEVICE},
      {"categoryBrowser", IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_BROWSER},
      {"categoryText", IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_TEXT},
      {"categoryWindowsAndDesks",
       IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_WINDOWS_AND_DESKS},
      {"categoryAccessibility",
       IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_ACCESSIBILITY},
      {"categoryDebug", IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_DEBUG},
      {"categoryDeveloper", IDS_SHORTCUT_CUSTOMIZATION_CATEGORY_DEVELOPER},
      {"shortcutWithConflictStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_SHORTCUT_WITH_CONFILICT_STATUS_MESSAGE},
      {"lockedShortcutStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_LOCKED_SHORTCUT_STATUS_MESSAGE},
      {"maxAcceleratorsReachedHint",
       IDS_SHORTCUT_CUSTOMIZATION_MAX_ACCELERATORS_REACHED_HINT},
      {"shiftOnlyNotAllowedStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_SHIFT_ONLY_NOT_ALLOWED_STATUS_MESSAGE},
      {"missingModifierStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_MISSING_MODIFIER_STATUS_MESSAGE},
      {"keyNotAllowedStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_KEY_NOT_ALLOWED_STATUS_MESSAGE},
      {"searchWithFunctionKeyNotAllowedStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_SEACH_WITH_FUNCTION_KEY_NOT_ALLOWED_STATUS_MESSAGE},
      {"warningSearchNotIncluded",
       IDS_SHORTCUT_CUSTOMIZATION_NON_SEARCH_SHORTCUT_WARNING},
      {"reservedKeyNotAllowedStatusMessage",
       IDS_SHORTCUT_CUSTOMIZATION_RESERVED_KEY_NOT_ALLOWED_STATUS_MESSAGE},
      {"nonStandardNotAllowedWithSearchMessage",
       IDS_SHORTCUT_CUSTOMIZATION_NON_STANDARD_KEY_WITH_SEARCH_MODIFIER},
      {"searchNoResults", IDS_SHORTCUT_CUSTOMIZATION_SEARCH_NO_RESULTS},
      {"searchClearQueryLabel",
       IDS_SHORTCUT_CUSTOMIZATION_SEARCH_CLEAR_QUERY_LABEL},
      {"searchPlaceholderLabel",
       IDS_SHORTCUT_CUSTOMIZATION_SEARCH_PLACEHOLDER_LABEL},
      {"acceleratorTextDivider",
       IDS_SHORTCUT_CUSTOMIZATION_ACCELERATOR_TEXT_DIVIDER},
      {"acceleratorRowAriaLabel",
       IDS_SHORTCUT_CUSTOMIZATION_ACCELERATOR_ROW_A11Y},
      {"acceleratorRowAriaLabelReadOnly",
       IDS_SHORTCUT_CUSTOMIZATION_ACCELERATOR_ROW_READ_ONLY_A11Y},
      {"editButtonAriaLabel", IDS_SHORTCUT_CUSTOMIZATION_EDIT_BUTTON_A11Y},
      {"cancelButtonAriaLabel", IDS_SHORTCUT_CUSTOMIZATION_CANCEL_BUTTON_A11Y},
      {"deleteButtonAriaLabel", IDS_SHORTCUT_CUSTOMIZATION_DELETE_BUTTON_A11Y},
      {"editDialogAriaLabel", IDS_SHORTCUT_CUSTOMIZATION_EDIT_DIALOG_A11Y},
      {"shortcutAdded", IDS_SHORTCUT_CUSTOMIZATION_SHORTCUT_ADDED_A11Y},
      {"shortcutEdited", IDS_SHORTCUT_CUSTOMIZATION_SHORTCUT_EDITED_A11Y},
      {"shortcutDeleted", IDS_SHORTCUT_CUSTOMIZATION_SHORTCUT_DELETED_A11Y},
      {"shortcutRestored", IDS_SHORTCUT_CUSTOMIZATION_SHORTCUT_RESTORED_A11Y},
      {"searchResultSelectedAriaLabel",
       IDS_SHORTCUT_CUSTOMIZATION_SEARCH_RESULT_ROW_A11Y_RESULT_SELECTED},
      {"inputKeyPlaceholder", IDS_SHORTCUT_CUSTOMIZATION_INPUT_KEY_PLACEHOLDER},
      {"subcategoryGeneralControls",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_GENERAL_CONTROLS},
      {"subcategoryApps", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_APPS},
      {"subcategoryMedia", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_MEDIA},
      {"subcategoryInputs", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_INPUTS},
      {"subcategoryDisplay", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_DISPLAY},
      {"subcategoryGeneral", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_GENERAL},
      {"subcategoryBrowserNavigation",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_BROWSER_NAVIGATION},
      {"subcategoryPages", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_PAGES},
      {"subcategoryTabs", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_TABS},
      {"subcategoryBookmarks",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_BOOKMARKS},
      {"subcategoryDeveloperTools",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_DEVELOPER_TOOLS},
      {"subcategoryTextNavigation",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_TEXT_NAVIGATION},
      {"subcategoryTextEditing",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_TEXT_EDITING},
      {"subcategoryWindows", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_WINDOWS},
      {"subcategoryDesks", IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_DESKS},
      {"subcategoryChromeVox",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_CHROMEVOX},
      {"subcategoryMouseKeys",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_MOUSE_KEYS},
      {"subcategoryVisibility",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_VISIBILITY},
      {"subcategoryAccessibilityNavigation",
       IDS_SHORTCUT_CUSTOMIZATION_SUBCATEGORY_ACCESSIBILITY_NAVIGATION},
      {"noShortcutAssigned", IDS_SHORTCUT_CUSTOMIZATION_NO_SHORTCUT_ASSIGNED},
      {"iconLabelAccessibility",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ACCESSIBILITY},
      {"iconLabelArrowDown", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ARROW_DOWN},
      {"iconLabelArrowLeft", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ARROW_LEFT},
      {"iconLabelArrowRight",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ARROW_RIGHT},
      {"iconLabelArrowUp", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ARROW_UP},
      {"iconLabelAudioVolumeDown",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_AUDIO_VOLUME_DOWN},
      {"iconLabelAudioVolumeMute",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_AUDIO_VOLUME_MUTE},
      {"iconLabelAudioVolumeUp",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_AUDIO_VOLUME_UP},
      {"iconLabelBrightnessDown",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BRIGHTNESS_DOWN},
      {"iconLabelBrightnessUp",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BRIGHTNESS_UP},
      {"iconLabelBrowserBack",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BROWSER_BACK},
      {"iconLabelBrowserForward",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BROWSER_FORWARD},
      {"iconLabelBrowserHome",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BROWSER_HOME},
      {"iconLabelBrowserRefresh",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BROWSER_REFRESH},
      {"iconLabelBrowserSearch",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_BROWSER_SEARCH},
      {"iconLabelContextMenu",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_CONTEXT_MENU},
      {"iconLabelEnableSelectToSpeak",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ENABLE_SELECT_TO_SPEAK},
      {"iconLabelEnableOrToggleDictation",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ENABLE_OR_TOGGLE_DICTATION},
      {"iconLabelEmojiPicker",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_EMOJI_PICKER},
      {"iconLabelKeyboardBacklightToggle",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_KEYBOARD_BACKLIGHT_TOGGLE},
      {"iconLabelKeyboardBrightnessUp",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_KEYBOARD_BRIGHTNESS_UP},
      {"iconLabelKeyboardBrightnessDown",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_KEYBOARD_BRIGHTNESS_DOWN},
      {"iconLabelLaunchApplication1",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_LAUNCH_APPLICATION1},
      {"iconLabelLaunchApplication2",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_LAUNCH_APPLICATION2},
      {"iconLabelLaunchAssistant",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_LAUNCH_ASSISTANT},
      {"iconLabelLaunchMail",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_LAUNCH_MAIL},
      {"iconLabelMediaFastForward",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MEDIA_FAST_FORWARD},
      {"iconLabelMediaPause",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MEDIA_PAUSE},
      {"iconLabelMediaPlay", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MEDIA_PLAY},
      {"iconLabelMediaPlayPause",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MEDIA_PLAY_PAUSE},
      {"iconLabelMediaTrackNext",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MEDIA_TRACK_NEXT},
      {"iconLabelMediaTrackPrevious",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MEDIA_TRACK_PREVIOUS},
      {"iconLabelMicrophoneMuteToggle",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MICROPHONE_MUTE_TOGGLE},
      {"iconLabelModeChange",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_MODE_CHANGE},
      {"iconLabelOpenLauncher",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_OPEN_LAUNCHER},
      {"iconLabelOpenSearch",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_OPEN_SEARCH},
      {"iconLabelPower", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_POWER},
      {"iconLabelPrintScreen",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_PRINT_SCREEN},
      {"iconLabelPrivacyScreenToggle",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_PRIVACY_SCREEN_TOGGLE},
      {"iconLabelSettings", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_SETTINGS},
      {"iconLabelStandby", IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_STAND_BY},
      {"iconLabelViewAllApps",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_VIEW_ALL_APPS},
      {"iconLabelZoomToggle",
       IDS_SHORTCUT_CUSTOMIZATION_ICON_LABEL_ZOOM_TOGGLE},
  };

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // For official builds, only add the real string if the feature is enabled.
  if (Shell::Get()->keyboard_capability()->IsModifierSplitEnabled()) {
    source->AddLocalizedString(
        "blockRightAltKey",
        IDS_SHORTCUT_CUSTOMIZATION_BLOCK_RIGHT_ALT_KEY_ERROR_MESSAGE);
    source->AddLocalizedString("iconLabelRightAlt",
                               IDS_KEYBOARD_RIGHT_ALT_LABEL);
  } else {
    source->AddLocalizedString("blockRightAltKey",
                               IDS_SHORTCUT_CUSTOMIZATION_BLOCK_RIGHT_ALT_KEY);
    source->AddLocalizedString(
        "iconLabelRightAlt", IDS_SHORTCUT_CUSTOMIZATION_INPUT_KEY_PLACEHOLDER);
  }
#else
  source->AddLocalizedString("blockRightAltKey",
                             IDS_SHORTCUT_CUSTOMIZATION_BLOCK_RIGHT_ALT_KEY);
  source->AddLocalizedString("iconLabelRightAlt",
                             IDS_SHORTCUT_CUSTOMIZATION_INPUT_KEY_PLACEHOLDER);
#endif
  source->AddLocalizedStrings(kLocalizedStrings);
  source->AddString("shortcutCustomizationLearnMoreUrl",
                    kShortcutCustomizationLearnMoreURL);
  source->UseStringsJs();
}

void AddFeatureFlags(content::WebUIDataSource* html_source) {
  html_source->AddBoolean(
      "isCustomizationAllowed",
      Shell::Get()->accelerator_prefs()->IsCustomizationAllowed());
  html_source->AddBoolean(
      "isJellyEnabledForShortcutCustomization",
      ash::features::IsJellyEnabledForShortcutCustomization());
  html_source->AddBoolean("isInputDeviceSettingsSplitEnabled",
                          features::IsInputDeviceSettingsSplitEnabled());
  html_source->AddBoolean(
      "hasFunctionKey",
      Shell::Get()->keyboard_capability()->HasFunctionKeyOnAnyKeyboard());
}

}  // namespace

ShortcutCustomizationAppUI::ShortcutCustomizationAppUI(content::WebUI* web_ui)
    : ui::MojoWebUIController(web_ui) {
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      web_ui->GetWebContents()->GetBrowserContext(),
      kChromeUIShortcutCustomizationAppHost);
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ScriptSrc,
      "script-src chrome://resources chrome://webui-test 'self';");

  ash::EnableTrustedTypesCSP(source);

  const auto resources =
      base::make_span(kAshShortcutCustomizationAppResources,
                      kAshShortcutCustomizationAppResourcesSize);
  SetUpWebUIDataSource(source, resources,
                       IDR_ASH_SHORTCUT_CUSTOMIZATION_APP_INDEX_HTML);
  AddLocalizedStrings(source);

  AddFeatureFlags(source);

  // Observe shortcut policy changes.
  if (Shell::Get()->accelerator_prefs()->IsUserEnterpriseManaged()) {
    Shell::Get()->accelerator_prefs()->AddObserver(this);
  }
}

ShortcutCustomizationAppUI::~ShortcutCustomizationAppUI() {
  if (Shell::Get()->accelerator_prefs()->IsUserEnterpriseManaged()) {
    Shell::Get()->accelerator_prefs()->RemoveObserver(this);
  }
}

void ShortcutCustomizationAppUI::OnShortcutPolicyUpdated() {
  base::Value::Dict update_data;
  // Update 'isCustomizationAllowed" when policy changes.
  update_data.Set("isCustomizationAllowed",
                  Shell::Get()->accelerator_prefs()->IsCustomizationAllowed());
  content::WebUIDataSource::Update(
      web_ui()->GetWebContents()->GetBrowserContext(),
      kChromeUIShortcutCustomizationAppHost, std::move(update_data));
}

void ShortcutCustomizationAppUI::BindInterface(
    mojo::PendingReceiver<
        shortcut_customization::mojom::AcceleratorConfigurationProvider>
        receiver) {
  shortcut_ui::ShortcutsAppManagerFactory::GetForBrowserContext(
      web_ui()->GetWebContents()->GetBrowserContext())
      ->accelerator_configuration_provider()
      ->BindInterface(std::move(receiver));
}

void ShortcutCustomizationAppUI::BindInterface(
    mojo::PendingReceiver<common::mojom::ShortcutInputProvider> receiver) {
  auto* shortcut_input_provider =
      shortcut_ui::ShortcutsAppManagerFactory::GetForBrowserContext(
          web_ui()->GetWebContents()->GetBrowserContext())
          ->shortcut_input_provider();
  auto* widget = views::Widget::GetWidgetForNativeWindow(
      web_ui()->GetWebContents()->GetTopLevelNativeWindow());
  if (widget) {
    shortcut_input_provider->TieProviderToWidget(widget);
  }
  shortcut_input_provider->BindInterface(std::move(receiver));
}

void ShortcutCustomizationAppUI::BindInterface(
    mojo::PendingReceiver<shortcut_customization::mojom::SearchHandler>
        receiver) {
  shortcut_ui::SearchHandler* search_handler =
      shortcut_ui::ShortcutsAppManagerFactory::GetForBrowserContext(
          web_ui()->GetWebContents()->GetBrowserContext())
          ->search_handler();

  // SearchHandler should not be a nullptr.
  DCHECK(search_handler);

  search_handler->BindInterface(std::move(receiver));
}

void ShortcutCustomizationAppUI::BindInterface(
    mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
  // BindInterface should not be called unless jelly-colors and
  // scanning-app-jelly flags are enabled.
  CHECK(features::IsJellyEnabledForShortcutCustomization());
  color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
      web_ui()->GetWebContents(), std::move(receiver));
}

WEB_UI_CONTROLLER_TYPE_IMPL(ShortcutCustomizationAppUI)
}  // namespace ash