| // Copyright 2024 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/searchbox/searchbox_handler.h" |
| |
| #include "base/base64.h" |
| #include "base/base64url.h" |
| #include "build/branding_buildflags.h" |
| #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/new_tab_page/new_tab_page_util.h" |
| #include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h" |
| #include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/bookmarks/browser/bookmark_model.h" |
| #include "components/omnibox/browser/omnibox_controller.h" |
| #include "components/omnibox/browser/omnibox_feature_configs.h" |
| #include "components/omnibox/browser/vector_icons.h" |
| #include "components/search/ntp_features.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/variations/variations_client.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "third_party/omnibox_proto/answer_data.pb.h" |
| #include "third_party/omnibox_proto/answer_type.pb.h" |
| #include "third_party/omnibox_proto/rich_answer_template.pb.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace { |
| |
| // TODO(niharm): convert back to constexpr char[] once feature is cleaned up |
| const char* kAnswerCurrencyIconResourceName = |
| "//resources/cr_components/searchbox/icons/currency.svg"; |
| constexpr char kAnswerDefaultIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/default.svg"; |
| const char* kAnswerDictionaryIconResourceName = |
| "//resources/cr_components/searchbox/icons/definition.svg"; |
| const char* kAnswerFinanceIconResourceName = |
| "//resources/cr_components/searchbox/icons/finance.svg"; |
| const char* kAnswerSunriseIconResourceName = |
| "//resources/cr_components/searchbox/icons/sunrise.svg"; |
| const char* kAnswerTranslationIconResourceName = |
| "//resources/cr_components/searchbox/icons/translation.svg"; |
| const char* kAnswerWhenIsIconResourceName = |
| "//resources/cr_components/searchbox/icons/when_is.svg"; |
| const char* kBookmarkIconResourceName = "//resources/images/icon_bookmark.svg"; |
| const char* kCalculatorIconResourceName = |
| "//resources/cr_components/searchbox/icons/calculator.svg"; |
| const char* kChromeProductIconResourceName = |
| "//resources/cr_components/searchbox/icons/chrome_product.svg"; |
| const char* kClockIconResourceName = "//resources/images/icon_clock.svg"; |
| const char* kDinoIconResourceName = |
| "//resources/cr_components/searchbox/icons/dino.svg"; |
| constexpr char kDriveDocsIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_docs.svg"; |
| constexpr char kDriveFolderIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_folder.svg"; |
| constexpr char kDriveFormIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_form.svg"; |
| constexpr char kDriveImageIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_image.svg"; |
| constexpr char kDriveLogoIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_logo.svg"; |
| constexpr char kDrivePdfIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_pdf.svg"; |
| constexpr char kDriveSheetsIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_sheets.svg"; |
| constexpr char kDriveSlidesIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_slides.svg"; |
| constexpr char kDriveVideoIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/drive_video.svg"; |
| constexpr char kExtensionAppIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/extension_app.svg"; |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| constexpr char kGoogleCalendarIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/calendar.svg"; |
| const char* kGoogleGIconResourceName = |
| "//resources/cr_components/searchbox/icons/google_g.svg"; |
| constexpr char kGoogleKeepNoteIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/note.svg"; |
| constexpr char kGoogleSitesIconResourceName[] = |
| "//resources/cr_components/searchbox/icons/sites.svg"; |
| #endif |
| const char* kHistoryIconResourceName = "//resources/images/icon_history.svg"; |
| const char* kIncognitoIconResourceName = |
| "//resources/cr_components/searchbox/icons/incognito.svg"; |
| const char* kJourneysIconResourceName = |
| "//resources/cr_components/searchbox/icons/journeys.svg"; |
| const char* kPageIconResourceName = |
| "//resources/cr_components/searchbox/icons/page.svg"; |
| const char* kPedalsIconResourceName = "//theme/current-channel-logo"; |
| const char* kSearchIconResourceName = "//resources/images/icon_search.svg"; |
| const char* kSparkIconResourceName = |
| "//resources/cr_components/searchbox/icons/spark.svg"; |
| const char* kStarActiveIconResourceName = |
| "//resources/cr_components/searchbox/icons/star_active.svg"; |
| const char* kTabIconResourceName = |
| "//resources/cr_components/searchbox/icons/tab.svg"; |
| const char* kTrendingUpIconResourceName = |
| "//resources/cr_components/searchbox/icons/trending_up.svg"; |
| |
| #if BUILDFLAG(IS_MAC) |
| const char* kMacShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/mac_share.svg"; |
| #elif BUILDFLAG(IS_WIN) |
| const char* kWinShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/win_share.svg"; |
| #elif BUILDFLAG(IS_LINUX) |
| const char* kLinuxShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/share.svg"; |
| #else |
| const char* kShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/share.svg"; |
| #endif |
| |
| static void DefineChromeRefreshRealboxIcons() { |
| kAnswerCurrencyIconResourceName = |
| "//resources/cr_components/searchbox/icons/currency_cr23.svg"; |
| kAnswerDictionaryIconResourceName = |
| "//resources/cr_components/searchbox/icons/definition_cr23.svg"; |
| kAnswerFinanceIconResourceName = |
| "//resources/cr_components/searchbox/icons/finance_cr23.svg"; |
| kAnswerSunriseIconResourceName = |
| "//resources/cr_components/searchbox/icons/sunrise_cr23.svg"; |
| kAnswerTranslationIconResourceName = |
| "//resources/cr_components/searchbox/icons/translation_cr23.svg"; |
| kAnswerWhenIsIconResourceName = |
| "//resources/cr_components/searchbox/icons/when_is_cr23.svg"; |
| kBookmarkIconResourceName = |
| "//resources/cr_components/searchbox/icons/bookmark_cr23.svg"; |
| kCalculatorIconResourceName = |
| "//resources/cr_components/searchbox/icons/calculator_cr23.svg"; |
| kChromeProductIconResourceName = |
| "//resources/cr_components/searchbox/icons/chrome_product_cr23.svg"; |
| kClockIconResourceName = |
| "//resources/cr_components/searchbox/icons/clock_cr23.svg"; |
| kDinoIconResourceName = |
| "//resources/cr_components/searchbox/icons/dino_cr23.svg"; |
| |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| kGoogleGIconResourceName = |
| "//resources/cr_components/searchbox/icons/google_g_cr23.svg"; |
| #endif |
| |
| kHistoryIconResourceName = |
| "//resources/cr_components/searchbox/icons/history_cr23.svg"; |
| kIncognitoIconResourceName = |
| "//resources/cr_components/searchbox/icons/incognito_cr23.svg"; |
| kJourneysIconResourceName = |
| "//resources/cr_components/searchbox/icons/journeys_cr23.svg"; |
| kPageIconResourceName = |
| "//resources/cr_components/searchbox/icons/page_cr23.svg"; |
| kPedalsIconResourceName = |
| "//resources/cr_components/searchbox/icons/chrome_product_cr23.svg"; |
| kSearchIconResourceName = |
| "//resources/cr_components/searchbox/icons/search_cr23.svg"; |
| kTabIconResourceName = |
| "//resources/cr_components/searchbox/icons/tab_cr23.svg"; |
| kTrendingUpIconResourceName = |
| "//resources/cr_components/searchbox/icons/trending_up_cr23.svg"; |
| |
| #if BUILDFLAG(IS_MAC) |
| kMacShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/mac_share_cr23.svg"; |
| #elif BUILDFLAG(IS_WIN) |
| kWinShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/win_share_cr23.svg"; |
| #elif BUILDFLAG(IS_LINUX) |
| kLinuxShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/share_cr23.svg"; |
| #else |
| kShareIconResourceName = |
| "//resources/cr_components/searchbox/icons/share_cr23.svg"; |
| #endif |
| } |
| |
| std::u16string GetAdditionalA11yMessage( |
| const AutocompleteMatch& match, |
| searchbox::mojom::SelectionLineState state) { |
| switch (state) { |
| case searchbox::mojom::SelectionLineState::kNormal: { |
| if (match.has_tab_match.value_or(false) && |
| base::FeatureList::IsEnabled(omnibox::kNtpRealboxPedals)) { |
| return l10n_util::GetStringUTF16(IDS_ACC_TAB_SWITCH_SUFFIX); |
| } |
| const OmniboxAction* action = match.GetActionAt(0u); |
| if (action) { |
| return action->GetLabelStrings().accessibility_suffix; |
| } |
| if (match.SupportsDeletion()) { |
| return l10n_util::GetStringUTF16(IDS_ACC_REMOVE_SUGGESTION_SUFFIX); |
| } |
| break; |
| } |
| case searchbox::mojom::SelectionLineState::kFocusedButtonRemoveSuggestion: |
| return l10n_util::GetStringUTF16( |
| IDS_ACC_REMOVE_SUGGESTION_FOCUSED_PREFIX); |
| default: |
| NOTREACHED_IN_MIGRATION(); |
| break; |
| } |
| return std::u16string(); |
| } |
| |
| std::optional<std::u16string> GetAdditionalText( |
| const SuggestionAnswer::ImageLine& line) { |
| if (line.additional_text()) { |
| const auto additional_text = line.additional_text()->text(); |
| if (!additional_text.empty()) { |
| return additional_text; |
| } |
| } |
| return std::nullopt; |
| } |
| |
| std::u16string ImageLineToString16(const SuggestionAnswer::ImageLine& line) { |
| std::vector<std::u16string> text; |
| for (const auto& text_field : line.text_fields()) { |
| text.push_back(text_field.text()); |
| } |
| const auto& additional_text = GetAdditionalText(line); |
| if (additional_text) { |
| text.push_back(additional_text.value()); |
| } |
| // TODO(crbug.com/40149768): Use placeholders or a l10n-friendly way to |
| // construct this string instead of concatenation. This currently only happens |
| // for stock ticker symbols. |
| return base::JoinString(text, u" "); |
| } |
| |
| bool MatchHasSideTypeAndRenderType( |
| const AutocompleteMatch& match, |
| omnibox::GroupConfig_SideType side_type, |
| omnibox::GroupConfig_RenderType render_type, |
| const omnibox::GroupConfigMap& suggestion_groups_map) { |
| omnibox::GroupId group_id = |
| match.suggestion_group_id.value_or(omnibox::GROUP_INVALID); |
| return base::Contains(suggestion_groups_map, group_id) && |
| suggestion_groups_map.at(group_id).side_type() == side_type && |
| suggestion_groups_map.at(group_id).render_type() == render_type; |
| } |
| |
| std::vector<searchbox::mojom::AutocompleteMatchPtr> CreateAutocompleteMatches( |
| const AutocompleteResult& result, |
| bookmarks::BookmarkModel* bookmark_model, |
| const omnibox::GroupConfigMap& suggestion_groups_map) { |
| std::vector<searchbox::mojom::AutocompleteMatchPtr> matches; |
| int line = 0; |
| for (const AutocompleteMatch& match : result) { |
| // Skip the primary column horizontal matches. This check guards against |
| // this unexpected scenario as the UI expects the primary column matches to |
| // be vertical ones. |
| if (MatchHasSideTypeAndRenderType( |
| match, omnibox::GroupConfig_SideType_DEFAULT_PRIMARY, |
| omnibox::GroupConfig_RenderType_HORIZONTAL, |
| suggestion_groups_map)) { |
| continue; |
| } |
| |
| // Skip the secondary column horizontal matches that are not entities or do |
| // not have images. This check guards against this unexpected scenario as |
| // the UI expects the secondary column horizontal matches to be entity |
| // suggestions with images. |
| if (MatchHasSideTypeAndRenderType( |
| match, omnibox::GroupConfig_SideType_SECONDARY, |
| omnibox::GroupConfig_RenderType_HORIZONTAL, |
| suggestion_groups_map) && |
| (match.type != AutocompleteMatchType::SEARCH_SUGGEST_ENTITY || |
| !match.image_url.is_valid())) { |
| continue; |
| } |
| |
| searchbox::mojom::AutocompleteMatchPtr mojom_match = |
| searchbox::mojom::AutocompleteMatch::New(); |
| mojom_match->allowed_to_be_default_match = |
| match.allowed_to_be_default_match; |
| mojom_match->contents = match.contents; |
| for (const auto& contents_class : match.contents_class) { |
| mojom_match->contents_class.push_back( |
| searchbox::mojom::ACMatchClassification::New(contents_class.offset, |
| contents_class.style)); |
| } |
| mojom_match->description = match.description; |
| for (const auto& description_class : match.description_class) { |
| mojom_match->description_class.push_back( |
| searchbox::mojom::ACMatchClassification::New( |
| description_class.offset, description_class.style)); |
| } |
| mojom_match->destination_url = match.destination_url; |
| mojom_match->suggestion_group_id = |
| match.suggestion_group_id.value_or(omnibox::GROUP_INVALID); |
| const bool is_bookmarked = |
| bookmark_model->IsBookmarked(match.destination_url); |
| mojom_match->icon_url = |
| SearchboxHandler::AutocompleteMatchVectorIconToResourceName( |
| match.GetVectorIcon(is_bookmarked)); |
| mojom_match->image_dominant_color = match.image_dominant_color; |
| mojom_match->image_url = match.image_url.spec(); |
| mojom_match->fill_into_edit = match.fill_into_edit; |
| mojom_match->inline_autocompletion = match.inline_autocompletion; |
| mojom_match->is_search_type = AutocompleteMatch::IsSearchType(match.type); |
| mojom_match->swap_contents_and_description = |
| match.swap_contents_and_description; |
| mojom_match->type = AutocompleteMatchType::ToString(match.type); |
| mojom_match->supports_deletion = match.SupportsDeletion(); |
| if (omnibox_feature_configs::SuggestionAnswerMigration::Get().enabled && |
| match.answer_template.has_value()) { |
| const omnibox::AnswerData& answer_data = |
| match.answer_template->answers(0); |
| const omnibox::FormattedString& headline = answer_data.headline(); |
| const std::string& headline_text = headline.text(); |
| // Grab the substring of headline starting after the first fragment text |
| // ends. Not making use of the first fragment because it contains the same |
| // data as `match.contents` but with HTML tags. |
| const std::u16string headline_substr = |
| base::UTF8ToUTF16(headline_text.substr( |
| headline.fragments(0).text().size(), |
| headline_text.size() - headline.fragments(0).text().size())); |
| |
| const auto& subhead_text = |
| base::UTF8ToUTF16(answer_data.subhead().text()); |
| // Reusing SuggestionAnswer because `headline` and `subhead` are |
| // equivalent to `first_line` and `second_line`. |
| mojom_match->answer = searchbox::mojom::SuggestionAnswer::New( |
| headline_substr.empty() |
| ? match.contents |
| : base::JoinString({match.contents, headline_substr}, u" "), |
| subhead_text); |
| mojom_match->image_url = answer_data.image().url(); |
| mojom_match->is_weather_answer_suggestion = |
| match.answer_type == omnibox::ANSWER_TYPE_WEATHER; |
| } else if (match.answer.has_value()) { |
| const auto& additional_text = |
| GetAdditionalText(match.answer->first_line()); |
| mojom_match->answer = searchbox::mojom::SuggestionAnswer::New( |
| additional_text ? base::JoinString( |
| {match.contents, additional_text.value()}, u" ") |
| : match.contents, |
| ImageLineToString16(match.answer->second_line())); |
| mojom_match->image_url = match.ImageUrl().spec(); |
| mojom_match->is_weather_answer_suggestion = |
| match.answer_type == omnibox::ANSWER_TYPE_WEATHER; |
| } |
| mojom_match->is_rich_suggestion = |
| !mojom_match->image_url.empty() || |
| match.type == AutocompleteMatchType::CALCULATOR || |
| match.answer_type != omnibox::ANSWER_TYPE_UNSPECIFIED; |
| if (base::FeatureList::IsEnabled(omnibox::kNtpRealboxPedals)) { |
| for (const auto& action : match.actions) { |
| const OmniboxAction::LabelStrings& label_strings = |
| action->GetLabelStrings(); |
| mojom_match->actions.emplace_back(searchbox::mojom::Action::New( |
| label_strings.accessibility_hint, label_strings.hint, |
| label_strings.suggestion_contents, |
| SearchboxHandler::ActionVectorIconToResourceName( |
| action->GetVectorIcon()))); |
| } |
| } |
| mojom_match->a11y_label = AutocompleteMatchType::ToAccessibilityLabel( |
| match, match.contents, line, 0, |
| GetAdditionalA11yMessage( |
| match, searchbox::mojom::SelectionLineState::kNormal)); |
| |
| mojom_match->remove_button_a11y_label = |
| AutocompleteMatchType::ToAccessibilityLabel( |
| match, match.contents, line, 0, |
| GetAdditionalA11yMessage(match, |
| searchbox::mojom::SelectionLineState:: |
| kFocusedButtonRemoveSuggestion)); |
| |
| mojom_match->tail_suggest_common_prefix = match.tail_suggest_common_prefix; |
| |
| matches.push_back(std::move(mojom_match)); |
| line++; |
| } |
| return matches; |
| } |
| |
| std::string GetBase64UrlVariations(Profile* profile) { |
| variations::VariationsClient* provider = profile->GetVariationsClient(); |
| |
| variations::mojom::VariationsHeadersPtr headers = |
| provider->GetVariationsHeaders(); |
| if (headers.is_null()) { |
| return std::string(); |
| } |
| const std::string variations_base64 = headers->headers_map.at( |
| variations::mojom::GoogleWebVisibility::FIRST_PARTY); |
| |
| // Variations headers are base64 encoded, however, we're attaching the value |
| // to a URL query parameter so they need to be base64url encoded. |
| std::string variations_decoded; |
| base::Base64Decode(variations_base64, &variations_decoded); |
| |
| std::string variations_base64url; |
| base::Base64UrlEncode(variations_decoded, |
| base::Base64UrlEncodePolicy::OMIT_PADDING, |
| &variations_base64url); |
| |
| return variations_base64url; |
| } |
| |
| base::flat_map<int32_t, searchbox::mojom::SuggestionGroupPtr> |
| CreateSuggestionGroupsMap( |
| const AutocompleteResult& result, |
| PrefService* prefs, |
| const omnibox::GroupConfigMap& suggestion_groups_map) { |
| base::flat_map<int32_t, searchbox::mojom::SuggestionGroupPtr> result_map; |
| for (const auto& pair : suggestion_groups_map) { |
| searchbox::mojom::SuggestionGroupPtr suggestion_group = |
| searchbox::mojom::SuggestionGroup::New(); |
| suggestion_group->header = base::UTF8ToUTF16(pair.second.header_text()); |
| suggestion_group->side_type = |
| static_cast<searchbox::mojom::SideType>(pair.second.side_type()); |
| suggestion_group->render_type = |
| static_cast<searchbox::mojom::RenderType>(pair.second.render_type()); |
| suggestion_group->hidden = |
| result.IsSuggestionGroupHidden(prefs, pair.first); |
| suggestion_group->show_group_a11y_label = l10n_util::GetStringFUTF16( |
| IDS_ACC_HEADER_SHOW_SUGGESTIONS_BUTTON, suggestion_group->header); |
| suggestion_group->hide_group_a11y_label = l10n_util::GetStringFUTF16( |
| IDS_ACC_HEADER_HIDE_SUGGESTIONS_BUTTON, suggestion_group->header); |
| |
| result_map.emplace(static_cast<int>(pair.first), |
| std::move(suggestion_group)); |
| } |
| return result_map; |
| } |
| |
| searchbox::mojom::AutocompleteResultPtr CreateAutocompleteResult( |
| const std::u16string& input, |
| const AutocompleteResult& result, |
| bookmarks::BookmarkModel* bookmark_model, |
| PrefService* prefs) { |
| return searchbox::mojom::AutocompleteResult::New( |
| input, |
| CreateSuggestionGroupsMap(result, prefs, result.suggestion_groups_map()), |
| CreateAutocompleteMatches(result, bookmark_model, |
| result.suggestion_groups_map())); |
| } |
| |
| } // namespace |
| |
| // static |
| void SearchboxHandler::SetupWebUIDataSource(content::WebUIDataSource* source, |
| Profile* profile, |
| bool enable_voice_search, |
| bool enable_lens_search) { |
| // Embedders which are served from chrome-untrusted:// URLs should override |
| // this to false. The chrome.timeTicks capability that the metrics reporter |
| // depends on is not defined in chrome-untrusted environments and attempting |
| // to use it will lead to a renderer process crash. See |
| // http://g/chrome-webui/haW6I9yt-uA/38ckX-aGAgAJ for details. |
| source->AddBoolean("reportMetrics", true); |
| |
| // The lens searchboxes overrides this to true to adjust various color and |
| // layout options. |
| source->AddBoolean("isLensSearchbox", false); |
| |
| static constexpr webui::LocalizedString kStrings[] = { |
| {"hideSuggestions", IDS_TOOLTIP_HEADER_HIDE_SUGGESTIONS_BUTTON}, |
| {"lensSearchButtonLabel", IDS_TOOLTIP_LENS_SEARCH}, |
| {"searchboxSeparator", IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR}, |
| {"removeSuggestion", IDS_OMNIBOX_REMOVE_SUGGESTION}, |
| {"searchBoxHint", IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MD}, |
| {"searchBoxHintMultimodal", IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MULTIMODAL}, |
| {"searchboxThumbnailLabel", |
| IDS_GOOGLE_SEARCH_BOX_MULTIMODAL_IMAGE_THUMBNAIL}, |
| {"showSuggestions", IDS_TOOLTIP_HEADER_SHOW_SUGGESTIONS_BUTTON}, |
| {"voiceSearchButtonLabel", IDS_TOOLTIP_MIC_SEARCH}}; |
| source->AddLocalizedStrings(kStrings); |
| |
| source->AddBoolean( |
| "searchboxMatchSearchboxTheme", |
| base::FeatureList::IsEnabled(ntp_features::kRealboxMatchSearchboxTheme)); |
| |
| bool redesigned_modules_enabled = |
| base::FeatureList::IsEnabled(ntp_features::kNtpModulesRedesigned); |
| source->AddString("searchboxWidthBehavior", |
| redesigned_modules_enabled ? "wide" : ""); |
| source->AddBoolean("realboxIsTall", redesigned_modules_enabled); |
| DefineChromeRefreshRealboxIcons(); |
| source->AddString( |
| "searchboxDefaultIcon", |
| base::FeatureList::IsEnabled(ntp_features::kRealboxUseGoogleGIcon) |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| ? kGoogleGIconResourceName |
| #else |
| ? kSearchIconResourceName |
| #endif |
| : kSearchIconResourceName); |
| |
| source->AddBoolean("searchboxVoiceSearch", enable_voice_search); |
| source->AddBoolean("searchboxLensSearch", enable_lens_search); |
| source->AddString("searchboxLensVariations", GetBase64UrlVariations(profile)); |
| source->AddBoolean( |
| "searchboxLensDirectUpload", |
| base::FeatureList::IsEnabled(ntp_features::kNtpLensDirectUpload)); |
| source->AddBoolean( |
| "searchboxCr23Theming", |
| base::FeatureList::IsEnabled(ntp_features::kRealboxCr23Theming)); |
| source->AddBoolean("searchboxCr23SteadyStateShadow", |
| ntp_features::kNtpRealboxCr23SteadyStateShadow.Get()); |
| } |
| |
| // static |
| std::string SearchboxHandler::AutocompleteMatchVectorIconToResourceName( |
| const gfx::VectorIcon& icon) { |
| if (icon.name == omnibox::kAnswerCurrencyIcon.name || |
| icon.name == omnibox::kAnswerCurrencyChromeRefreshIcon.name) { |
| return kAnswerCurrencyIconResourceName; |
| } else if (icon.name == omnibox::kAnswerDefaultIcon.name) { |
| return kAnswerDefaultIconResourceName; |
| } else if (icon.name == omnibox::kAnswerDictionaryIcon.name || |
| icon.name == omnibox::kAnswerDictionaryChromeRefreshIcon.name) { |
| return kAnswerDictionaryIconResourceName; |
| } else if (icon.name == omnibox::kAnswerFinanceIcon.name || |
| icon.name == omnibox::kAnswerFinanceChromeRefreshIcon.name) { |
| return kAnswerFinanceIconResourceName; |
| } else if (icon.name == omnibox::kAnswerSunriseIcon.name || |
| icon.name == omnibox::kAnswerSunriseChromeRefreshIcon.name) { |
| return kAnswerSunriseIconResourceName; |
| } else if (icon.name == omnibox::kAnswerTranslationIcon.name || |
| icon.name == omnibox::kAnswerTranslationChromeRefreshIcon.name) { |
| return kAnswerTranslationIconResourceName; |
| } else if (icon.name == omnibox::kAnswerWhenIsIcon.name || |
| icon.name == omnibox::kAnswerWhenIsChromeRefreshIcon.name) { |
| return kAnswerWhenIsIconResourceName; |
| } else if (icon.name == omnibox::kBookmarkIcon.name || |
| icon.name == omnibox::kBookmarkChromeRefreshIcon.name) { |
| return kBookmarkIconResourceName; |
| } else if (icon.name == omnibox::kCalculatorIcon.name || |
| icon.name == omnibox::kCalculatorChromeRefreshIcon.name) { |
| return kCalculatorIconResourceName; |
| } else if (icon.name == omnibox::kClockIcon.name || |
| icon.name == omnibox::kClockChromeRefreshIcon.name) { |
| return kClockIconResourceName; |
| } else if (icon.name == omnibox::kDriveDocsIcon.name) { |
| return kDriveDocsIconResourceName; |
| } else if (icon.name == omnibox::kDriveFolderIcon.name) { |
| return kDriveFolderIconResourceName; |
| } else if (icon.name == omnibox::kDriveFormsIcon.name) { |
| return kDriveFormIconResourceName; |
| } else if (icon.name == omnibox::kDriveImageIcon.name) { |
| return kDriveImageIconResourceName; |
| } else if (icon.name == omnibox::kDriveLogoIcon.name) { |
| return kDriveLogoIconResourceName; |
| } else if (icon.name == omnibox::kDrivePdfIcon.name) { |
| return kDrivePdfIconResourceName; |
| } else if (icon.name == omnibox::kDriveSheetsIcon.name) { |
| return kDriveSheetsIconResourceName; |
| } else if (icon.name == omnibox::kDriveSlidesIcon.name) { |
| return kDriveSlidesIconResourceName; |
| } else if (icon.name == omnibox::kDriveVideoIcon.name) { |
| return kDriveVideoIconResourceName; |
| } else if (icon.name == omnibox::kExtensionAppIcon.name) { |
| return kExtensionAppIconResourceName; |
| } else if (icon.name == vector_icons::kHistoryIcon.name || |
| icon.name == vector_icons::kHistoryChromeRefreshIcon.name) { |
| return kHistoryIconResourceName; |
| } else if (icon.name == omnibox::kJourneysIcon.name || |
| icon.name == omnibox::kJourneysChromeRefreshIcon.name) { |
| return kJourneysIconResourceName; |
| } else if (icon.name == omnibox::kPageIcon.name || |
| icon.name == omnibox::kPageChromeRefreshIcon.name) { |
| return kPageIconResourceName; |
| } else if (icon.name == omnibox::kProductIcon.name || |
| icon.name == omnibox::kProductChromeRefreshIcon.name) { |
| return kChromeProductIconResourceName; |
| } else if (icon.name == vector_icons::kSearchIcon.name || |
| icon.name == vector_icons::kSearchChromeRefreshIcon.name) { |
| return kSearchIconResourceName; |
| } else if (icon.name == omnibox::kTrendingUpIcon.name || |
| icon.name == omnibox::kTrendingUpChromeRefreshIcon.name) { |
| return kTrendingUpIconResourceName; |
| } else if (icon.is_empty()) { |
| return ""; // An empty resource name is effectively a blank icon. |
| } else { |
| return ActionVectorIconToResourceName(icon); |
| } |
| } |
| |
| // static |
| std::string SearchboxHandler::ActionVectorIconToResourceName( |
| const gfx::VectorIcon& icon) { |
| if (icon.name == omnibox::kSwitchIcon.name || |
| icon.name == omnibox::kSwitchCr2023Icon.name) { |
| return kTabIconResourceName; |
| } |
| if (icon.name == omnibox::kDinoIcon.name || |
| icon.name == omnibox::kDinoCr2023Icon.name) { |
| return kDinoIconResourceName; |
| } |
| if (icon.name == omnibox::kDriveFormsIcon.name) { |
| return kDriveFormIconResourceName; |
| } |
| if (icon.name == omnibox::kDriveDocsIcon.name) { |
| return kDriveDocsIconResourceName; |
| } |
| if (icon.name == omnibox::kDriveSheetsIcon.name) { |
| return kDriveSheetsIconResourceName; |
| } |
| if (icon.name == omnibox::kDriveSlidesIcon.name) { |
| return kDriveSlidesIconResourceName; |
| } |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| if (icon.name == vector_icons::kGoogleCalendarIcon.name) { |
| return kGoogleCalendarIconResourceName; |
| } |
| if (icon.name == vector_icons::kGoogleKeepNoteIcon.name) { |
| return kGoogleKeepNoteIconResourceName; |
| } |
| if (icon.name == vector_icons::kGoogleSitesIcon.name) { |
| return kGoogleSitesIconResourceName; |
| } |
| if (icon.name == vector_icons::kGoogleSuperGIcon.name || |
| icon.name == vector_icons::kGoogleGLogoMonochromeIcon.name) { |
| return kGoogleGIconResourceName; |
| } |
| #endif |
| if (icon.name == omnibox::kIncognitoIcon.name || |
| icon.name == omnibox::kIncognitoCr2023Icon.name) { |
| return kIncognitoIconResourceName; |
| } |
| if (icon.name == omnibox::kJourneysIcon.name || |
| icon.name == omnibox::kJourneysChromeRefreshIcon.name) { |
| return kJourneysIconResourceName; |
| } |
| if (icon.name == omnibox::kPedalIcon.name || |
| icon.name == omnibox::kProductChromeRefreshIcon.name) { |
| return kPedalsIconResourceName; |
| } |
| #if BUILDFLAG(IS_MAC) |
| if (icon.name == omnibox::kShareMacIcon.name || |
| icon.name == omnibox::kShareMacChromeRefreshIcon.name) { |
| return kMacShareIconResourceName; |
| } |
| #elif BUILDFLAG(IS_WIN) |
| if (icon.name == omnibox::kShareWinIcon.name || |
| icon.name == omnibox::kShareWinChromeRefreshIcon.name) { |
| return kWinShareIconResourceName; |
| } |
| #elif BUILDFLAG(IS_LINUX) |
| if (icon.name == omnibox::kShareIcon.name || |
| icon.name == omnibox::kShareLinuxChromeRefreshIcon.name) { |
| return kLinuxShareIconResourceName; |
| } |
| #else |
| if (icon.name == omnibox::kShareIcon.name || |
| icon.name == omnibox::kShareChromeRefreshIcon.name) { |
| return kShareIconResourceName; |
| } |
| #endif |
| if (icon.name == omnibox::kSparkIcon.name) { |
| return kSparkIconResourceName; |
| } |
| if (icon.name == omnibox::kStarActiveIcon.name || |
| icon.name == omnibox::kStarActiveChromeRefreshIcon.name) { |
| return kStarActiveIconResourceName; |
| } |
| NOTREACHED_IN_MIGRATION() |
| << "Every vector icon returned by OmniboxAction::GetVectorIcon " |
| "must have an equivalent SVG resource for the NTP Realbox. " |
| "icon.name: '" |
| << icon.name << "'"; |
| return ""; |
| } |
| |
| SearchboxHandler::SearchboxHandler( |
| mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler, |
| Profile* profile, |
| content::WebContents* web_contents, |
| MetricsReporter* metrics_reporter) |
| : profile_(profile), |
| web_contents_(web_contents), |
| metrics_reporter_(metrics_reporter), |
| page_set_(false), |
| page_handler_(this, std::move(pending_page_handler)) {} |
| |
| SearchboxHandler::~SearchboxHandler() { |
| // Avoids dangling pointer warning when `controller_` is not owned. |
| controller_ = nullptr; |
| } |
| |
| void SearchboxHandler::OnResultChanged(AutocompleteController* controller, |
| bool default_match_changed) { |
| if (metrics_reporter_ && !metrics_reporter_->HasLocalMark("ResultChanged")) { |
| metrics_reporter_->Mark("ResultChanged"); |
| } |
| |
| page_->AutocompleteResultChanged(CreateAutocompleteResult( |
| autocomplete_controller()->input().text(), |
| autocomplete_controller()->result(), |
| BookmarkModelFactory::GetForBrowserContext(profile_), |
| profile_->GetPrefs())); |
| |
| // The owned OmniboxController does not observe the AutocompleteController. |
| // Notify the prerender here to start preloading if the results are ready. |
| // TODO(crbug.com/40062053): Make the owned OmniboxController observe the |
| // AutocompleteController and move this logic to the RealboxOmniboxClient. |
| if (owned_controller_) { |
| if (autocomplete_controller()->done()) { |
| if (SearchPrefetchService* search_prefetch_service = |
| SearchPrefetchServiceFactory::GetForProfile(profile_)) { |
| search_prefetch_service->OnResultChanged( |
| web_contents_, autocomplete_controller()->result()); |
| } |
| } |
| } |
| } |
| |
| OmniboxController* SearchboxHandler::omnibox_controller() const { |
| return controller_; |
| } |
| |
| AutocompleteController* SearchboxHandler::autocomplete_controller() const { |
| return omnibox_controller()->autocomplete_controller(); |
| } |