// Copyright 2015 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/history/history_ui.h" #include #include #include #include #include "base/command_line.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/weak_ptr.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "build/build_config.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/feature_engagement/tracker_factory.h" #include "chrome/browser/glic/glic_pref_names.h" #include "chrome/browser/glic/public/glic_enabling.h" #include "chrome/browser/glic/public/glic_keyed_service.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/history_embeddings/history_embeddings_utils.h" #include "chrome/browser/page_image_service/image_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_avatar_icon_util.h" #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/signin/account_preview_data_service_factory.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/signin_ui_util.h" #include "chrome/browser/sync/sync_service_factory.h" #include "chrome/browser/ui/browser_element_identifiers.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/webui/cr_components/history/history_util.h" #include "chrome/browser/ui/webui/cr_components/history_clusters/history_clusters_util.h" #include "chrome/browser/ui/webui/cr_components/history_embeddings/history_embeddings_handler.h" #include "chrome/browser/ui/webui/favicon_source.h" #include "chrome/browser/ui/webui/history/browsing_history_handler.h" #include "chrome/browser/ui/webui/history/foreign_session_handler.h" #include "chrome/browser/ui/webui/history/history_identity_state_watcher.h" #include "chrome/browser/ui/webui/history/history_login_handler.h" #include "chrome/browser/ui/webui/history/navigation_handler.h" #include "chrome/browser/ui/webui/history_clusters/history_clusters_handler.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/theme_source.h" #include "chrome/browser/ui/webui/user_education/user_education_handler.h" #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/history_resources.h" #include "chrome/grit/history_resources_map.h" #include "components/critical_actions/core/browser/features.h" #include "components/grit/components_scaled_resources.h" #include "components/history/core/browser/features.h" #include "components/history/core/common/pref_names.h" #include "components/history_clusters/core/config.h" #include "components/history_clusters/core/features.h" #include "components/history_clusters/core/history_clusters_prefs.h" #include "components/history_embeddings/core/history_embeddings_features.h" #include "components/page_image_service/image_service.h" #include "components/page_image_service/image_service_handler.h" #include "components/prefs/pref_service.h" #include "components/sessions/core/session_types.h" #include "components/signin/public/base/signin_buildflags.h" #include "components/signin/public/base/signin_pref_names.h" #include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/strings/grit/components_strings.h" #include "components/sync/base/features.h" #include "components/user_education/webui/user_education.mojom.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.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/tracked_element/tracked_element_handler_document_singleton.h" #include "ui/webui/webui_util.h" #if BUILDFLAG(ENABLE_DICE_SUPPORT) #include "chrome/browser/ui/webui/history/history_cross_device_signin_promo_handler.h" #endif namespace { content::WebUIDataSource* CreateAndAddHistoryUIHTMLSource(Profile* profile) { content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd( profile, chrome::kChromeUIHistoryHost); source->AddBoolean("replaceSyncPromosWithSignInPromos", syncer::IsReplaceSyncPromosWithSignInPromosEnabled()); #if !BUILDFLAG(IS_CHROMEOS) source->AddBoolean("unoPhase2FollowUp", base::FeatureList::IsEnabled(syncer::kUnoPhase2FollowUp)); #endif // BUILDFLAG!(IS_CHROMEOS) HistoryUtil::PopulateCommonSourceForHistory(source, profile); static constexpr webui::LocalizedString kStrings[] = { // Localized strings (alphabetical order). {"compareHistoryEmpty", IDS_COMPARE_HISTORY_EMPTY}, {"compareHistoryRemove", IDS_COMPARE_HISTORY_REMOVE}, {"compareHistoryHeader", IDS_COMPARE_HISTORY_HEADER}, {"compareHistoryInfo", IDS_COMPARE_HISTORY_INFO}, {"compareHistoryListsMenuItem", IDS_COMPARE_HISTORY_MENU_ITEM}, {"compareHistoryRow", IDS_COMPARE_HISTORY_ROW}, {"compareHistoryMenuAriaLabel", IDS_COMPARE_HISTORY_MENU_ARIA_LABEL}, {"noSyncedResults", IDS_HISTORY_NO_SYNCED_RESULTS}, {"signinOnPhonePromoButton", IDS_HISTORY_SIGNIN_ON_PHONE_PROMO_BUTTON}, {"signinOnPhonePromoSubtitle", IDS_HISTORY_SIGNIN_ON_PHONE_PROMO_SUBTITLE}, {"signinOnPhonePromoTitle", IDS_HISTORY_SIGNIN_ON_PHONE_PROMO_TITLE}, {"turnOnSyncPromo", IDS_HISTORY_TURN_ON_SYNC_PROMO}, {"turnOnSyncPromoDesc", IDS_HISTORY_TURN_ON_SYNC_PROMO_DESC}, {"turnOnSyncHistoryPromo", IDS_HISTORY_SYNC_HISTORY_PROMO}, {"syncHistoryPromoBodySignedOut", IDS_RECENT_TABS_SYNC_HISTORY_PROMO_BODY_SIGNED_OUT}, {"syncHistoryPromoBodyPendingSignIn", IDS_RECENT_TABS_SYNC_HISTORY_PROMO_BODY_PENDING_SIGN_IN}, {"syncHistoryPromoBodyPendingSignInSyncHistoryOn", IDS_RECENT_TABS_SYNC_HISTORY_PROMO_BODY_PENDING_SIGN_IN_SYNC_HISTORY_ON}, {"verifyItsYou", IDS_VERIFY_IT_IS_YOU}}; source->AddLocalizedStrings(kStrings); source->AddLocalizedString("turnOnSyncHistoryButton", IDS_HISTORY_SYNC_HISTORY_BUTTON); source->AddString("accountPictureUrl", profiles::GetPlaceholderAvatarIconUrl()); const bool is_critical_actions_enabled = base::FeatureList::IsEnabled( critical_actions::features::kCriticalActionHistory); // The history page footer can display messages about other forms of // browsing history, linking to Google My Activity (GMA) and/or // Gemini Apps Activity (GAA). At most one message is shown, depending on // the user's settings. source->AddString( "sidebarFooterGMAOnly", l10n_util::GetStringFUTF16(IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_ONLY, chrome::kMyActivityUrlInHistory)); source->AddString( "sidebarFooterGAAOnly", l10n_util::GetStringFUTF16(IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GAA_ONLY, chrome::kMyActivityGeminiAppsUrl)); source->AddString( "sidebarFooterGMAAndGAA", l10n_util::GetStringFUTF16( is_critical_actions_enabled ? IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_AND_GAA_CRITICAL_ACTIONS : IDS_HISTORY_OTHER_FORMS_OF_HISTORY_GMA_AND_GAA, chrome::kMyActivityUrlInHistory, chrome::kMyActivityGeminiAppsUrl)); // Links that are used in the messages above. source->AddString("sidebarFooterGMALink", chrome::kMyActivityUrlInHistory); source->AddString("sidebarFooterGAALink", chrome::kMyActivityGeminiAppsUrl); const bool is_glic_enabled = glic::GlicEnabling::ShouldShowSettingsPage(profile); auto* glic_service = glic::GlicKeyedService::Get(profile); const bool is_glic_web_actuation_available = glic::GlicEnabling::IsEnabledAndConsentForProfile(profile) && glic_service && glic_service->enabling().GetUserEnabledActuationOnWeb(); source->AddBoolean("isGlicEnabled", is_glic_enabled); source->AddBoolean("isGlicWebActuationAvailable", is_glic_web_actuation_available); #if BUILDFLAG(IS_CHROMEOS) source->AddLocalizedString("turnOnSyncButton", IDS_HISTORY_TURN_ON_SYNC_BUTTON); #else signin::IdentityManager* identity_manager = IdentityManagerFactory::GetForProfile(profile); bool has_primary_account = identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin); AccountInfo account_info = signin_ui_util::GetSingleAccountForPromos( identity_manager, AccountPreviewDataServiceFactory::GetForProfile(profile)); source->AddString( "historySyncPromoBodySignedIn", l10n_util::GetStringFUTF16(IDS_HISTORY_SYNC_PROMO_BODY_SIGNED_IN, base::UTF8ToUTF16(account_info.GetEmail()))); source->AddString( "turnOnSignedInSyncHistoryPromoBodySignInSyncOff", l10n_util::GetStringFUTF16( IDS_RECENT_TABS_SYNC_HISTORY_PROMO_BODY_SIGNED_IN_SYNC_OFF, base::UTF8ToUTF16(account_info.GetEmail()))); source->AddString("accountName", account_info.GetFullName().value_or("")); source->AddString("accountEmail", account_info.GetEmail()); if (!has_primary_account && !account_info.IsEmpty()) { source->AddString( "turnOnSyncButton", l10n_util::GetStringFUTF16( IDS_PROFILES_DICE_WEB_ONLY_SIGNIN_BUTTON, base::UTF8ToUTF16(account_info.GetGivenName().value_or( account_info.GetEmail())))); } else { source->AddLocalizedString("turnOnSyncButton", IDS_HISTORY_TURN_ON_SYNC_BUTTON); } static constexpr webui::LocalizedString kHistorySyncStrings[] = { {"historySyncPromoTitle", IDS_HISTORY_SYNC_PROMO_TITLE}, {"historySyncPromoBodySignedOut", IDS_HISTORY_SYNC_PROMO_BODY_SIGNED_OUT}, {"historySyncPromoBodySignInPending", IDS_HISTORY_SYNC_PROMO_BODY_SIGN_IN_PENDING}, {"historySyncPromoBodySignInPendingSyncHistoryOn", IDS_HISTORY_SYNC_PROMO_BODY_SIGN_IN_PENDING_SYNC_HISTORY_ON}, }; source->AddLocalizedStrings(kHistorySyncStrings); #endif // BUILDFLAG(IS_CHROMEOS) bool enable_history_embeddings = history_embeddings::IsHistoryEmbeddingsEnabledForProfile(profile); source->AddBoolean("enableHistoryEmbeddings", enable_history_embeddings); source->AddBoolean( "maybeShowEmbeddingsIph", history_embeddings::IsHistoryEmbeddingsSettingVisible(profile) && !enable_history_embeddings); static constexpr webui::LocalizedString kHistoryEmbeddingsStrings[] = { {"historyEmbeddingsPromoLabel", IDS_HISTORY_EMBEDDINGS_PROMO_LABEL}, {"historyEmbeddingsPromoClose", IDS_HISTORY_EMBEDDINGS_PROMO_CLOSE}, {"historyEmbeddingsPromoHeading", IDS_HISTORY_EMBEDDINGS_PROMO_HEADING}, {"historyEmbeddingsPromoBody", IDS_HISTORY_EMBEDDINGS_PROMO_BODY}, {"historyEmbeddingsAnswersPromoHeading", IDS_HISTORY_EMBEDDINGS_ANSWERS_PROMO_HEADING}, {"historyEmbeddingsAnswersPromoBody", IDS_HISTORY_EMBEDDINGS_ANSWERS_PROMO_BODY}, {"historyEmbeddingsPromoSettingsLinkText", IDS_HISTORY_EMBEDDIGNS_PROMO_SETTINGS_LINK_TEXT}, }; source->AddLocalizedStrings(kHistoryEmbeddingsStrings); source->AddBoolean("isBrowsingHistoryActorIntegrationM3Enabled", history::IsBrowsingHistoryActorIntegrationM3Enabled()); source->AddBoolean("isCriticalActionsEnabled", is_critical_actions_enabled); source->AddString("webuiRefresh2026", features::IsWebuiRefresh2026Enabled() ? "webui-refresh-2026" : ""); // History clusters HistoryClustersUtil::PopulateSource(source, profile, /*in_side_panel=*/false); return source; } } // namespace HistoryUIConfig::HistoryUIConfig() : WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIHistoryHost) {} HistoryUIConfig::~HistoryUIConfig() = default; std::unique_ptr HistoryUIConfig::CreateWebUIController(content::WebUI* web_ui, const GURL& url) { Profile* profile = Profile::FromWebUI(web_ui); if (profile->IsGuestSession()) { return std::make_unique( web_ui, chrome::kChromeUIHistoryHost); } return std::make_unique(web_ui); } HistoryUI::HistoryUI(content::WebUI* web_ui) : ui::MojoWebUIController(web_ui, /*enable_chrome_send=*/true) { Profile* profile = Profile::FromWebUI(web_ui); content::WebUIDataSource* data_source = CreateAndAddHistoryUIHTMLSource(profile); ManagedUIHandler::Initialize(web_ui, data_source); content::URLDataSource::Add(profile, std::make_unique(profile)); pref_change_registrar_.Init(profile->GetPrefs()); pref_change_registrar_.Add(history_clusters::prefs::kVisible, base::BindRepeating(&HistoryUI::UpdateDataSource, base::Unretained(this))); web_ui->AddMessageHandler(std::make_unique()); web_ui->AddMessageHandler(std::make_unique()); web_ui->AddMessageHandler( std::make_unique(base::BindRepeating( &HistoryUI::UpdateDataSource, base::Unretained(this)))); ui::TrackedElementHandlerDocumentSingleton::Register( this, std::vector{kHistorySearchInputElementId, kHistoryGeminiFilterChipElementId}); } HistoryUI::~HistoryUI() = default; WEB_UI_CONTROLLER_TYPE_IMPL(HistoryUI) DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HistoryUI, kHistoryGeminiFilterChipElementId); // static scoped_refptr HistoryUI::GetFaviconResourceBytes( ui::ResourceScaleFactor scale_factor) { return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale( IDR_HISTORY_FAVICON, scale_factor); } void HistoryUI::BindInterface( mojo::PendingReceiver pending_page_handler_factory) { history_embeddings_handler_factory_receiver_.reset(); history_embeddings_handler_factory_receiver_.Bind( std::move(pending_page_handler_factory)); } void HistoryUI::CreatePageHandler( mojo::PendingRemote page, mojo::PendingReceiver receiver) { history_embeddings_handler_ = std::make_unique( std::move(receiver), std::move(page), Profile::FromWebUI(web_ui())->GetWeakPtr(), web_ui(), /*for_side_panel=*/false); } void HistoryUI::BindInterface( mojo::PendingReceiver pending_page_handler) { browsing_history_handler_ = std::make_unique( std::move(pending_page_handler), Profile::FromWebUI(web_ui()), web_ui()->GetWebContents()); } #if BUILDFLAG(ENABLE_DICE_SUPPORT) void HistoryUI::BindInterface( mojo::PendingReceiver pending_receiver) { history_cross_device_signin_promo_handler_ = std::make_unique( std::move(pending_receiver), web_ui()->GetWebContents()); } #endif void HistoryUI::BindInterface( mojo::PendingReceiver pending_receiver) { foreign_session_page_handler_factory_receiver_.reset(); foreign_session_page_handler_factory_receiver_.Bind( std::move(pending_receiver)); } void HistoryUI::CreateForeignSessionPageHandler( mojo::PendingRemote page, mojo::PendingReceiver receiver) { foreign_session_handler_ = std::make_unique( std::move(receiver), std::move(page), Profile::FromWebUI(web_ui()), web_ui()->GetWebContents(), base::BindRepeating([](content::WebContents* source_web_contents, const ::sessions::SessionTab& tab, WindowOpenDisposition disposition) { SessionRestore::RestoreForeignSessionTab(source_web_contents, tab, disposition); }), base::BindRepeating( [](Profile* profile, const std::vector& windows) { SessionRestore::RestoreForeignSessionWindows( profile, windows.begin(), windows.end(), base::DoNothing()); }), /*side_panel_ui=*/nullptr); } void HistoryUI::BindInterface( mojo::PendingReceiver pending_page_handler_factory) { history_clusters_handler_factory_receiver_.reset(); history_clusters_handler_factory_receiver_.Bind( std::move(pending_page_handler_factory)); } void HistoryUI::CreatePageHandler( mojo::PendingRemote page, mojo::PendingReceiver receiver) { history_clusters_handler_ = std::make_unique( std::move(receiver), std::move(page), Profile::FromWebUI(web_ui()), web_ui()->GetWebContents(), // HistoryUI should always be in a tab. Look it up unconditionally. tabs::TabInterface::GetFromContents(web_ui()->GetWebContents())); } void HistoryUI::BindInterface( mojo::PendingReceiver pending_page_handler) { base::WeakPtr image_service_weak; if (auto* image_service = page_image_service::ImageServiceFactory::GetForBrowserContext( Profile::FromWebUI(web_ui()))) { image_service_weak = image_service->GetWeakPtr(); } image_service_handler_ = std::make_unique( std::move(pending_page_handler), std::move(image_service_weak)); } void HistoryUI::UpdateDataSource() { CHECK(web_ui()); Profile* profile = Profile::FromWebUI(web_ui()); base::DictValue update; const bool is_managed = profile->GetPrefs()->IsManagedPreference( history_clusters::prefs::kVisible); // History clusters are always visible unless the visibility prefs // is set to false by policy. update.Set( kIsHistoryClustersVisibleKey, profile->GetPrefs()->GetBoolean(history_clusters::prefs::kVisible) || !is_managed); content::WebUIDataSource::Update(profile, chrome::kChromeUIHistoryHost, std::move(update)); } void HistoryUI::BindInterface( mojo::PendingReceiver pending_receiver) { if (help_bubble_handler_factory_receiver_.is_bound()) { help_bubble_handler_factory_receiver_.reset(); } help_bubble_handler_factory_receiver_.Bind(std::move(pending_receiver)); } void HistoryUI::CreateHelpBubbleHandler( mojo::PendingRemote client, mojo::PendingReceiver handler) { help_bubble_handler_ = std::make_unique( std::move(handler), std::move(client), ui::TrackedElementHandlerDocumentSingleton::GetOrCreate( web_ui()->GetRenderFrameHost())); } void HistoryUI::BindInterface( mojo::PendingReceiver< user_education::mojom::UserEducationMixedTrustHandlerFactory> pending_receiver) { user_education_handler_factory_receiver_.reset(); user_education_handler_factory_receiver_.Bind(std::move(pending_receiver)); } void HistoryUI::CreateUserEducationMixedTrustHandler( mojo::PendingReceiver receiver) { user_education_handler_ = std::make_unique( std::move(receiver), web_ui()->GetWebContents()); }