blob: 7d95e771b62b85b3de1601e337fb4e64bcecf1f4 [file] [log] [blame]
// Copyright 2025 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/omnibox/omnibox_next_features.h"
#include <string>
#include "base/base64.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/autocomplete/aim_eligibility_service_factory.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/grit/generated_resources.h"
#include "components/contextual_search/contextual_search_metrics_recorder.h"
#include "components/omnibox/browser/aim_eligibility_service.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
constexpr base::FeatureState DISABLED = base::FEATURE_DISABLED_BY_DEFAULT;
} // namespace
namespace omnibox {
namespace internal {
// If enabled, Omnibox popup will transition to AI-Mode with the compose-box
// panel taking up the whole of the popup, covering the location bar completely.
BASE_FEATURE(kWebUIOmniboxAimPopup, DISABLED);
} // namespace internal
constexpr base::FeatureParam<AddContextButtonVariant>::Option
kAddContextButtonVariantOptions[] = {
{AddContextButtonVariant::kNone, "none"},
{AddContextButtonVariant::kBelowResults, "below_results"},
{AddContextButtonVariant::kAboveResults, "above_results"},
{AddContextButtonVariant::kInline, "inline"}};
// Configures the placement of the "Add Context" button in the Omnibox popup.
const base::FeatureParam<AddContextButtonVariant>
kWebUIOmniboxAimPopupAddContextButtonVariantParam{
&internal::kWebUIOmniboxAimPopup, "AddContextButtonVariant",
AddContextButtonVariant::kNone, &kAddContextButtonVariantOptions};
// If enabled, removes the cutout for the location bar and fills the entire
// popup content with the WebUI WebView.
BASE_FEATURE(kWebUIOmniboxFullPopup, DISABLED);
// If enabled, shows the omnibox suggestions in the popup in WebUI.
BASE_FEATURE(kWebUIOmniboxPopup, DISABLED);
// Enables the WebUI for omnibox suggestions without modifying the popup UI.
BASE_FEATURE(kWebUIOmniboxPopupDebug, DISABLED);
// Enables side-by-side comparison omnibox suggestions in WebUI and Views.
const base::FeatureParam<bool> kWebUIOmniboxPopupDebugSxSParam{
&kWebUIOmniboxPopupDebug, "SxS", false};
// Decodes a proto object from its serialized Base64 string representation.
// Returns true if decoding and parsing succeed, false otherwise.
bool ParseProtoFromBase64String(const std::string& input,
google::protobuf::MessageLite& output) {
if (input.empty()) {
return false;
}
std::string decoded_input;
// Decode the Base64-encoded input string into decoded_input.
if (!base::Base64Decode(input, &decoded_input)) {
return false;
}
if (decoded_input.empty()) {
return false;
}
// Parse the decoded string into the proto object.
return output.ParseFromString(decoded_input);
}
// Populates and returns the Composebox configuration proto.
omnibox::NTPComposeboxConfig GetNTPComposeboxConfig() {
// Initialize the default config.
omnibox::NTPComposeboxConfig default_config;
default_config.mutable_entry_point()->set_num_page_load_animations(3);
auto* composebox = default_config.mutable_composebox();
composebox->set_close_by_escape(kCloseComposeboxByEscape.Get());
composebox->set_close_by_click_outside(kCloseComposeboxByClickOutside.Get());
auto* image_upload = composebox->mutable_image_upload();
image_upload->set_enable_webp_encoding(false);
image_upload->set_downscale_max_image_size(1500000);
image_upload->set_downscale_max_image_width(1600);
image_upload->set_downscale_max_image_height(1600);
image_upload->set_image_compression_quality(40);
image_upload->set_mime_types_allowed("image/*");
auto* attachment_upload = composebox->mutable_attachment_upload();
attachment_upload->set_max_size_bytes(200000000);
attachment_upload->set_mime_types_allowed(".pdf,application/pdf");
composebox->set_max_num_files(kMaxNumFiles.Get());
composebox->set_input_placeholder_text(
l10n_util::GetStringUTF8(IDS_NTP_COMPOSE_PLACEHOLDER_TEXT));
composebox->set_is_pdf_upload_enabled(true);
auto* placeholder_config = composebox->mutable_placeholder_config();
placeholder_config->set_change_text_animation_interval_ms(4000);
placeholder_config->set_fade_text_animation_duration_ms(250);
placeholder_config->add_placeholders(
omnibox::NTPComposeboxConfig_PlaceholderConfig_Placeholder_ASK);
placeholder_config->add_placeholders(
omnibox::NTPComposeboxConfig_PlaceholderConfig_Placeholder_PLAN);
placeholder_config->add_placeholders(
omnibox::NTPComposeboxConfig_PlaceholderConfig_Placeholder_COMPARE);
placeholder_config->add_placeholders(
omnibox::NTPComposeboxConfig_PlaceholderConfig_Placeholder_RESEARCH);
placeholder_config->add_placeholders(
omnibox::NTPComposeboxConfig_PlaceholderConfig_Placeholder_TEACH);
placeholder_config->add_placeholders(
omnibox::NTPComposeboxConfig_PlaceholderConfig_Placeholder_WRITE);
// Attempt to parse the config proto from the feature parameter if it is set.
omnibox::NTPComposeboxConfig fieldtrial_config;
if (!kConfigParam.Get().empty()) {
bool parsed =
ParseProtoFromBase64String(kConfigParam.Get(), fieldtrial_config);
contextual_search::ContextualSearchMetricsRecorder::
RecordConfigParseSuccess(
contextual_search::ContextualSearchSource::kOmnibox, parsed);
if (!parsed) {
return default_config;
}
// A present `MimeTypesAllowed` message will clear the image and attachment
// `mime_types` value.
if (fieldtrial_config.composebox()
.image_upload()
.has_mime_types_allowed()) {
image_upload->clear_mime_types_allowed();
}
if (fieldtrial_config.composebox()
.attachment_upload()
.has_mime_types_allowed()) {
attachment_upload->clear_mime_types_allowed();
}
}
// Merge the fieldtrial config into the default config.
//
// Note: The `MergeFrom()` method will append repeated fields from
// `fieldtrial_config` to `default_config`. Since the intent is to override
// the values of repeated fields in `default_config` with the values from
// `fieldtrial_config`, the repeated fields in `default_config` must be
// cleared before calling `MergeFrom()` iff the repeated fields have been set
// in `fieldtrial_config`.
default_config.MergeFrom(fieldtrial_config);
return default_config;
}
bool IsAimPopupFeatureEnabled() {
return base::FeatureList::IsEnabled(internal::kWebUIOmniboxAimPopup);
}
bool IsAimPopupEnabled(Profile* profile) {
if (!profile) {
return false;
}
if (!IsAimPopupFeatureEnabled()) {
return false;
}
auto* aim_service = AimEligibilityServiceFactory::GetForProfile(profile);
return aim_service && aim_service->IsAimEligible();
}
bool IsCreateImagesEnabled(Profile* profile) {
if (!profile) {
return false;
}
if (!IsAimPopupFeatureEnabled()) {
return false;
}
if (kShowToolsAndModels.Get() && kShowCreateImageTool.Get() &&
kForceToolsAndModels.Get()) {
return true;
}
AimEligibilityService* aim_eligibility_service =
AimEligibilityServiceFactory::GetForProfile(profile);
return kShowToolsAndModels.Get() && kShowCreateImageTool.Get() &&
aim_eligibility_service &&
aim_eligibility_service->IsCreateImagesEligible();
}
bool IsDeepSearchEnabled(Profile* profile) {
if (!profile) {
return false;
}
if (!IsAimPopupFeatureEnabled()) {
return false;
}
if (kShowToolsAndModels.Get() && kForceToolsAndModels.Get()) {
return true;
}
AimEligibilityService* aim_eligibility_service =
AimEligibilityServiceFactory::GetForProfile(profile);
return kShowToolsAndModels.Get() && aim_eligibility_service &&
aim_eligibility_service->IsDeepSearchEligible();
}
std::unique_ptr<
contextual_search::ContextualSearchContextController::ConfigParams>
CreateQueryControllerConfigParams() {
auto config_params = std::make_unique<
contextual_search::ContextualSearchContextController::ConfigParams>();
config_params->send_lns_surface = kSendLnsSurfaceParam.Get();
config_params->suppress_lns_surface_param_if_no_image =
kSuppressLnsSurfaceParamIfNoImage.Get();
config_params->enable_multi_context_input_flow = kMaxNumFiles.Get() > 1;
config_params->enable_viewport_images = kEnableViewportImages.Get();
config_params->use_separate_request_ids_for_multi_context_viewport_images =
kUseSeparateRequestIdsForMultiContextViewportImages.Get();
return config_params;
}
const base::FeatureParam<bool> kCloseComposeboxByClickOutside(
&internal::kWebUIOmniboxAimPopup,
"CloseComposeboxByClickOutside",
true);
const base::FeatureParam<bool> kCloseComposeboxByEscape(
&internal::kWebUIOmniboxAimPopup,
"CloseComposeboxByEscape",
true);
const base::FeatureParam<std::string>
kConfigParam(&internal::kWebUIOmniboxAimPopup, "ConfigParam", "");
const base::FeatureParam<bool> kContextMenuEnableMultiTabSelection(
&internal::kWebUIOmniboxAimPopup,
"ContextMenuEnableMultiTabSelection",
false);
const base::FeatureParam<int> kContextMenuMaxTabSuggestions(
&internal::kWebUIOmniboxAimPopup,
"ContextMenuMaxTabSuggestions",
5);
const base::FeatureParam<bool> kEnableViewportImages(
&internal::kWebUIOmniboxAimPopup,
"EnableViewportImages",
true);
const base::FeatureParam<bool> kForceToolsAndModels(
&internal::kWebUIOmniboxAimPopup,
"ForceToolsAndModels",
false);
const base::FeatureParam<int> kMaxNumFiles(&internal::kWebUIOmniboxAimPopup,
"MaxNumFiles",
1);
const base::FeatureParam<bool> kSendLnsSurfaceParam(
&internal::kWebUIOmniboxAimPopup,
"SendLnsSurfaceParam",
true);
const base::FeatureParam<bool> kShowComposeboxImageSuggestions(
&internal::kWebUIOmniboxAimPopup,
"ShowComposeboxImageSuggestions",
false);
const base::FeatureParam<bool> kShowComposeboxTypedSuggest(
&internal::kWebUIOmniboxAimPopup,
"ShowComposeboxTypedSuggest",
true);
const base::FeatureParam<bool> kShowComposeboxZps(
&internal::kWebUIOmniboxAimPopup,
"ShowComposeboxZps",
true);
const base::FeatureParam<bool>
kShowContextMenu(&internal::kWebUIOmniboxAimPopup, "ShowContextMenu", true);
const base::FeatureParam<bool> kShowContextMenuDescription(
&internal::kWebUIOmniboxAimPopup,
"ShowContextMenuDescription",
true);
const base::FeatureParam<bool> kShowContextMenuTabPreviews(
&internal::kWebUIOmniboxAimPopup,
"ShowContextMenuTabPreviews",
true);
const base::FeatureParam<bool> kShowCreateImageTool(
&internal::kWebUIOmniboxAimPopup,
"ShowCreateImageTool",
false);
const base::FeatureParam<bool> kShowRecentTabChip(
&internal::kWebUIOmniboxAimPopup,
"ShowRecentTabChip",
true);
const base::FeatureParam<bool> kShowSmartCompose(
&internal::kWebUIOmniboxAimPopup,
"ShowSmartCompose",
true);
const base::FeatureParam<bool> kShowSubmit(&internal::kWebUIOmniboxAimPopup,
"ShowSubmit",
true);
const base::FeatureParam<bool> kShowToolsAndModels(
&internal::kWebUIOmniboxAimPopup,
"ShowToolsAndModels",
false);
const base::FeatureParam<bool> kSuppressLnsSurfaceParamIfNoImage(
&internal::kWebUIOmniboxAimPopup,
"SuppressLnsSurfaceParamIfNoImage",
true);
const base::FeatureParam<bool>
kUseSeparateRequestIdsForMultiContextViewportImages(
&internal::kWebUIOmniboxAimPopup,
"UseSeparateRequestIdsForMultiContextViewportImages",
false);
FeatureConfig::FeatureConfig() : config(GetNTPComposeboxConfig()) {}
FeatureConfig::FeatureConfig(const FeatureConfig&) = default;
FeatureConfig::FeatureConfig(FeatureConfig&&) = default;
FeatureConfig& FeatureConfig::operator=(const FeatureConfig&) = default;
FeatureConfig& FeatureConfig::operator=(FeatureConfig&&) = default;
FeatureConfig::~FeatureConfig() = default;
} // namespace omnibox