| // 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(¶ms); |
| } |