910e62b5创建于 1月15日历史提交
// 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/browser_command/browser_command_handler.h"

#include "base/command_line.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/command_updater_impl.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/feedback/show_feedback_page.h"
#include "chrome/browser/new_tab_page/promos/promo_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/customize_chrome/side_panel_controller.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/tabs/split_tab_metrics.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/user_education/tutorial_identifiers.h"
#include "chrome/browser/user_education/user_education_service.h"
#include "chrome/browser/user_education/user_education_service_factory.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/webui_url_constants.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/performance_manager/public/features.h"
#include "components/safe_browsing/content/browser/web_ui/safe_browsing_ui.h"
#include "components/safe_browsing/core/common/safe_browsing_policy_handler.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/common/safebrowsing_referral_methods.h"
#include "components/saved_tab_groups/public/features.h"
#include "components/tabs/public/tab_interface.h"
#include "components/user_education/common/tutorial/tutorial_identifier.h"
#include "components/user_education/common/tutorial/tutorial_service.h"
#include "net/base/url_util.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"

#if BUILDFLAG(ENABLE_GLIC)
#include "chrome/browser/glic/glic_settings_util.h"
#include "chrome/browser/glic/public/glic_enabling.h"
#include "chrome/browser/glic/public/glic_keyed_service.h"
#include "chrome/browser/glic/public/glic_keyed_service_factory.h"
#include "chrome/browser/glic/widget/glic_window_controller.h"
#include "chrome/browser/ui/webui/webui_embedding_context.h"
#endif  // BUILDFLAG(ENABLE_GLIC)

using browser_command::mojom::ClickInfoPtr;
using browser_command::mojom::Command;
using browser_command::mojom::CommandHandler;

// static
const char BrowserCommandHandler::kPromoBrowserCommandHistogramName[] =
    "NewTabPage.Promos.PromoBrowserCommand";

BrowserCommandHandler::BrowserCommandHandler(
    mojo::PendingReceiver<CommandHandler> pending_page_handler,
    Profile* profile,
    std::vector<browser_command::mojom::Command> supported_commands,
    content::WebContents* web_contents)
    : profile_(profile),
      supported_commands_(supported_commands),
      command_updater_(std::make_unique<CommandUpdaterImpl>(this)),
      page_handler_(this, std::move(pending_page_handler)),
      web_contents_(web_contents) {
  if (supported_commands_.empty()) {
    return;
  }

  EnableSupportedCommands();
}

BrowserCommandHandler::~BrowserCommandHandler() = default;

void BrowserCommandHandler::CanExecuteCommand(
    browser_command::mojom::Command command_id,
    CanExecuteCommandCallback callback) {
  if (!base::Contains(supported_commands_, command_id)) {
    std::move(callback).Run(false);
    return;
  }

  bool can_execute = false;
  switch (static_cast<Command>(command_id)) {
    case Command::kUnknownCommand:
      // Nothing to do.
      break;
    case Command::kOpenSafetyCheck:
      can_execute = !enterprise_util::IsBrowserManaged(profile_);
      break;
    case Command::kOpenSafeBrowsingEnhancedProtectionSettings: {
      bool managed = safe_browsing::SafeBrowsingPolicyHandler::
          IsSafeBrowsingProtectionLevelSetByPolicy(profile_->GetPrefs());
      bool already_enabled =
          safe_browsing::IsEnhancedProtectionEnabled(*(profile_->GetPrefs()));
      can_execute = !managed && !already_enabled;
    } break;
    case Command::kOpenFeedbackForm:
      can_execute = true;
      break;
    case Command::kOpenPrivacyGuide:
      can_execute =
          !enterprise_util::IsBrowserManaged(profile_) && !profile_->IsChild();
      base::UmaHistogramBoolean("Privacy.Settings.PrivacyGuide.CanShowNTPPromo",
                                can_execute);
      break;
    case Command::kStartTabGroupTutorial:
    case Command::kStartSavedTabGroupTutorial:
      can_execute = TutorialServiceExists() && BrowserSupportsTabGroups();
      break;
    case Command::kOpenPasswordManager:
      can_execute = true;
      break;
    case Command::kNoOpCommand:
      can_execute = true;
      break;
    case Command::kOpenPerformanceSettings:
      can_execute = true;
      break;
    case Command::kOpenNTPAndStartCustomizeChromeTutorial:
      can_execute = TutorialServiceExists() && DefaultSearchProviderIsGoogle();
      break;
    case Command::kStartPasswordManagerTutorial:
      can_execute = TutorialServiceExists();
      break;
    case Command::kOpenAutofillSettings:
      can_execute = true;
      break;
    case Command::kOpenAISettings:
      can_execute = true;
      break;
    case Command::kOpenSafetyCheckFromWhatsNew:
      can_execute = true;
      break;
    case Command::kOpenPaymentsSettings:
      can_execute = true;
      break;
    case Command::kOpenGlic:
      can_execute = true;
      break;
    case Command::kOpenGlicSettings:
      can_execute = true;
      break;
    case Command::kPrewarmGlicFre:
      can_execute = true;
      break;
    case Command::kOpenSplitView:
      // What's new module is gated on the kSideBySide flag already.
      can_execute = true;
      break;
  }
  std::move(callback).Run(can_execute);
}

void BrowserCommandHandler::ExecuteCommand(Command command_id,
                                           ClickInfoPtr click_info,
                                           ExecuteCommandCallback callback) {
  if (!base::Contains(supported_commands_, command_id)) {
    std::move(callback).Run(false);
    return;
  }

  const auto disposition = ui::DispositionFromClick(
      click_info->middle_button, click_info->alt_key, click_info->ctrl_key,
      click_info->meta_key, click_info->shift_key);
  const bool command_executed =
      GetCommandUpdater()->ExecuteCommandWithDisposition(
          static_cast<int>(command_id), disposition);
  std::move(callback).Run(command_executed);
}

void BrowserCommandHandler::ExecuteCommandWithDisposition(
    int id,
    WindowOpenDisposition disposition) {
  const auto command = static_cast<Command>(id);
  base::UmaHistogramEnumeration(kPromoBrowserCommandHistogramName, command);

  switch (command) {
    case Command::kUnknownCommand:
      // Nothing to do.
      break;
    case Command::kOpenSafetyCheck:
      NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kSafetyCheckSubPage)),
                    disposition);
      base::RecordAction(
          base::UserMetricsAction("NewTabPage_Promos_SafetyCheck"));
      break;
    case Command::kOpenSafeBrowsingEnhancedProtectionSettings:
      NavigateToEnhancedProtectionSetting();
      base::RecordAction(
          base::UserMetricsAction("NewTabPage_Promos_EnhancedProtection"));
      break;
    case Command::kOpenFeedbackForm:
      OpenFeedbackForm();
      break;
    case Command::kOpenPrivacyGuide:
      NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kPrivacyGuideSubPage)),
                    disposition);
      base::RecordAction(
          base::UserMetricsAction("NewTabPage_Promos_PrivacyGuide"));
      break;
    case Command::kStartTabGroupTutorial:
    case Command::kStartSavedTabGroupTutorial:
      StartTabGroupTutorial();
      break;
    case Command::kOpenPasswordManager:
      OpenPasswordManager();
      break;
    case Command::kNoOpCommand:
      // Nothing to do.
      break;
    case Command::kOpenPerformanceSettings:
      NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kPerformanceSubPage)),
                    disposition);
      break;
    case Command::kOpenNTPAndStartCustomizeChromeTutorial:
      OpenNTPAndStartCustomizeChromeTutorial();
      break;
    case Command::kStartPasswordManagerTutorial:
      StartPasswordManagerTutorial();
      break;
    case Command::kOpenAutofillSettings:
      NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kAutofillSubPage)),
                    disposition);
      break;
    case Command::kOpenAISettings:
      OpenAISettings();
      break;
    case Command::kOpenSafetyCheckFromWhatsNew:
      NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kSafetyCheckSubPage)),
                    disposition);
      break;
    case Command::kOpenPaymentsSettings:
      NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kPaymentsSubPage)),
                    disposition);
      break;
    case Command::kOpenGlic: {
      OpenGlic();
      break;
    }
    case Command::kOpenGlicSettings:
      OpenGlicSettings();
      break;
    case Command::kPrewarmGlicFre:
      PrewarmGlicFre();
      break;
    case Command::kOpenSplitView:
      OpenSplitView();
      break;
    default:
      NOTREACHED() << "Unspecified behavior for command " << id;
  }
}

void BrowserCommandHandler::OnTutorialStarted(
    user_education::TutorialIdentifier tutorial_id,
    user_education::TutorialService* tutorial_service) {
  if (tutorial_service) {
    tutorial_service->LogStartedFromWhatsNewPage(
        tutorial_id, tutorial_service->IsRunningTutorial(tutorial_id));
  }
}

void BrowserCommandHandler::StartTutorial(StartTutorialInPage::Params params) {
  auto* browser = chrome::FindBrowserWithProfile(profile_);
  StartTutorialInPage::Start(browser, std::move(params));
}

bool BrowserCommandHandler::TutorialServiceExists() {
  auto* service = UserEducationServiceFactory::GetForBrowserContext(profile_);
  auto* tutorial_service = service ? &service->tutorial_service() : nullptr;
  return !!tutorial_service;
}

bool BrowserCommandHandler::BrowserSupportsTabGroups() {
  Browser* browser = chrome::FindBrowserWithProfile(profile_);
  return browser->tab_strip_model()->SupportsTabGroups();
}

void BrowserCommandHandler::StartTabGroupTutorial() {
  auto* tutorial_id = kTabGroupTutorialId;

  if (BrowserSupportsTabGroups()) {
    StartTutorialInPage::Params params;
    params.tutorial_id = tutorial_id;
    params.callback = base::BindOnce(&BrowserCommandHandler::OnTutorialStarted,
                                     base::Unretained(this), tutorial_id);
    StartTutorial(std::move(params));
  }
}

void BrowserCommandHandler::NavigateToEnhancedProtectionSetting() {
  chrome::ShowSafeBrowsingEnhancedProtectionWithIph(
      chrome::FindBrowserWithProfile(profile_),
      safe_browsing::SafeBrowsingSettingReferralMethod::kPromoSlingerReferral);
}

void BrowserCommandHandler::OpenPasswordManager() {
  chrome::ShowPasswordManager(chrome::FindBrowserWithProfile(profile_));
}

void BrowserCommandHandler::OpenAISettings() {
  chrome::ShowSettingsSubPage(chrome::FindBrowserWithProfile(profile_),
                              chrome::kExperimentalAISettingsSubPage);
}

bool BrowserCommandHandler::DefaultSearchProviderIsGoogle() {
  return search::DefaultSearchProviderIsGoogle(profile_);
}

bool BrowserCommandHandler::BrowserSupportsSavedTabGroups() {
  Browser* browser = chrome::FindBrowserWithProfile(profile_);

  // Duplicated from chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
  // Which cannot be included here
  return browser->profile()->IsRegularProfile();
}

void BrowserCommandHandler::OpenNTPAndStartCustomizeChromeTutorial() {
  auto* tutorial_id = kSidePanelCustomizeChromeTutorialId;

  if (DefaultSearchProviderIsGoogle()) {
    StartTutorialInPage::Params params;
    params.tutorial_id = tutorial_id;
    params.callback = base::BindOnce(&BrowserCommandHandler::OnTutorialStarted,
                                     base::Unretained(this), tutorial_id);
    params.target_url = GURL(chrome::kChromeUINewTabPageURL);
    StartTutorial(std::move(params));
  }
}

void BrowserCommandHandler::StartPasswordManagerTutorial() {
  auto* tutorial_id = kPasswordManagerTutorialId;

  StartTutorialInPage::Params params;
  params.tutorial_id = tutorial_id;
  params.callback = base::BindOnce(&BrowserCommandHandler::OnTutorialStarted,
                                   base::Unretained(this), tutorial_id);
  StartTutorial(std::move(params));
}

void BrowserCommandHandler::StartSavedTabGroupTutorial() {
  auto* tutorial_id = kSavedTabGroupTutorialId;

  StartTutorialInPage::Params params;
  params.tutorial_id = tutorial_id;
  params.callback = base::BindOnce(&BrowserCommandHandler::OnTutorialStarted,
                                   base::Unretained(this), tutorial_id);
  StartTutorial(std::move(params));
}

void BrowserCommandHandler::OpenGlic() {
#if BUILDFLAG(ENABLE_GLIC)

  glic::GlicKeyedService* glic_service = glic::GlicKeyedService::Get(profile_);

  if (!glic_service) {
    return;
  }

  auto* browser_window = webui::GetBrowserWindowInterface(web_contents_);

  glic_service->window_controller().Toggle(
      browser_window, /*prevent_close=*/false,
      glic::mojom::InvocationSource::kWhatsNew,
      /*prompt_suggestion=*/std::nullopt);
#endif  // BUILDFLAG(ENABLE_GLIC)
}

void BrowserCommandHandler::OpenGlicSettings() {
#if BUILDFLAG(ENABLE_GLIC)
  if (glic::GlicEnabling::ShouldShowSettingsPage(profile_)) {
    glic::OpenGlicKeyboardShortcutSetting(profile_);
  } else {
    // Link to help center article.
    auto* command_line = base::CommandLine::ForCurrentProcess();
    bool has_url =
        command_line->HasSwitch(::switches::kGlicShortcutsLearnMoreURL);
    const std::string url = has_url
                                ? command_line->GetSwitchValueASCII(
                                      ::switches::kGlicShortcutsLearnMoreURL)
                                : features::kGlicShortcutsLearnMoreURL.Get();
    if (url.empty()) {
      return;
    }

    std::string ks_param;
#if BUILDFLAG(IS_WIN)
    ks_param = "chrome_ks_win";
#elif BUILDFLAG(IS_MAC)
    ks_param = "chrome_ks_mac";
#endif
    NavigateToURL(net::AppendOrReplaceQueryParameter(GURL(url), "p", ks_param),
                  WindowOpenDisposition::SINGLETON_TAB);
  }
#endif
}

void BrowserCommandHandler::PrewarmGlicFre() {
#if BUILDFLAG(ENABLE_GLIC)
  glic::GlicKeyedService* glic_service = glic::GlicKeyedService::Get(profile_);
  if (glic_service) {
    glic_service->TryPreloadFre(glic::GlicPrewarmingFreSource::kBrowserCommand);
  }
#endif  // BUILDFLAG(ENABLE_GLIC)
}

void BrowserCommandHandler::OpenSplitView() {
  tabs::TabInterface* tab =
      tabs::TabInterface::MaybeGetFromContents(web_contents_);
  if (tab && !tab->IsSplit()) {
    chrome::NewSplitTab(tab->GetBrowserWindowInterface(),
                        split_tabs::SplitTabCreatedSource::kWhatsNew);
  }
}

void BrowserCommandHandler::OpenFeedbackForm() {
  chrome::ShowFeedbackPage(feedback_settings_.url, profile_,
                           feedback_settings_.source,
                           std::string() /* description_template */,
                           std::string() /* description_placeholder_text */,
                           feedback_settings_.category /* category_tag */,
                           std::string() /* extra_diagnostics */);
}

void BrowserCommandHandler::ConfigureFeedbackCommand(
    FeedbackCommandSettings settings) {
  feedback_settings_ = settings;
}

void BrowserCommandHandler::EnableSupportedCommands() {
  // Explicitly enable supported commands.
  GetCommandUpdater()->UpdateCommandEnabled(
      static_cast<int>(Command::kUnknownCommand), true);
  for (Command command : supported_commands_) {
    GetCommandUpdater()->UpdateCommandEnabled(static_cast<int>(command), true);
  }
}

CommandUpdater* BrowserCommandHandler::GetCommandUpdater() {
  return command_updater_.get();
}

void BrowserCommandHandler::NavigateToURL(const GURL& url,
                                          WindowOpenDisposition disposition) {
  NavigateParams params(profile_, url, ui::PAGE_TRANSITION_LINK);
  params.disposition = disposition;
  Navigate(&params);
}