blob: 0e5da000ed583e6d7735799ef87d311e49de72b8 [file] [log] [blame]
// 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/lens/lens_side_panel_untrusted_ui.h"
#include "base/strings/strcat.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/lens/lens_composebox_controller.h"
#include "chrome/browser/ui/lens/lens_overlay_controller.h"
#include "chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h"
#include "chrome/browser/ui/lens/lens_overlay_theme_utils.h"
#include "chrome/browser/ui/lens/lens_search_controller.h"
#include "chrome/browser/ui/lens/lens_search_feature_flag_utils.h"
#include "chrome/browser/ui/lens/lens_searchbox_controller.h"
#include "chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/lens_shared_resources.h"
#include "chrome/grit/lens_shared_resources_map.h"
#include "chrome/grit/lens_untrusted_resources.h"
#include "chrome/grit/lens_untrusted_resources_map.h"
#include "components/lens/lens_features.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "third_party/lens_server_proto/aim_communication.pb.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/webui/color_change_listener/color_change_handler.h"
#include "ui/webui/resources/cr_components/composebox/composebox.mojom.h"
#include "ui/webui/webui_util.h"
namespace lens {
LensSidePanelUntrustedUI::LensSidePanelUntrustedUI(content::WebUI* web_ui)
: UntrustedTopChromeWebUIController(web_ui) {
// Set up the chrome-untrusted://lens/ source.
content::WebUIDataSource* html_source =
content::WebUIDataSource::CreateAndAdd(
web_ui->GetWebContents()->GetBrowserContext(),
chrome::kChromeUILensUntrustedSidePanelURL);
html_source->AddLocalizedString("backButton", IDS_ACCNAME_BACK);
html_source->AddLocalizedString("close", IDS_CLOSE);
html_source->AddLocalizedString("dismiss",
IDS_LENS_OVERLAY_TOAST_DISMISS_MESSAGE);
html_source->AddLocalizedString(
"networkErrorPageTopLine",
IDS_SIDE_PANEL_LENS_OVERLAY_GENERIC_ERROR_PAGE_FIRST_LINE);
html_source->AddLocalizedString(
"networkErrorPageBottomLine",
IDS_SIDE_PANEL_LENS_OVERLAY_GENERIC_ERROR_PAGE_SECOND_LINE);
html_source->AddLocalizedString(
"protectedErrorPageTopLine",
IDS_SIDE_PANEL_LENS_OVERLAY_PROTECTED_PAGE_ERROR_FIRST_LINE);
html_source->AddLocalizedString(
"protectedErrorPageBottomLine",
IDS_SIDE_PANEL_LENS_OVERLAY_PROTECTED_PAGE_ERROR_SECOND_LINE);
html_source->AddLocalizedString(
"searchboxGhostLoaderHintTextPrimaryDefault",
lens::features::ShouldUseAltLoadingHintWeb()
? IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_LOADING_HINT_PRIMARY_ALT
: IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_LOADING_HINT_PRIMARY);
html_source->AddLocalizedString(
"searchboxGhostLoaderHintTextPrimaryPdf",
lens::features::ShouldUseAltLoadingHintPdf()
? IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_LOADING_HINT_PRIMARY_ALT
: IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_LOADING_HINT_PRIMARY_PDF);
html_source->AddLocalizedString(
"searchboxGhostLoaderHintTextSecondary",
IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_LOADING_HINT_SECONDARY);
html_source->AddLocalizedString("searchboxGhostLoaderErrorText",
IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_ERROR_TEXT);
html_source->AddLocalizedString(
"searchboxGhostLoaderNoSuggestText",
IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_NO_SUGGEST_TEXT);
html_source->AddLocalizedString(
"feedbackToastMessage", lens::features::IsLensUpdatedFeedbackEnabled()
? IDS_LENS_OVERLAY_FEEDBACK_TOAST_MESSAGE_ALT
: IDS_LENS_OVERLAY_FEEDBACK_TOAST_MESSAGE);
html_source->AddLocalizedString("sendFeedbackButtonText",
IDS_LENS_OVERLAY_SEND_FEEDBACK_BUTTON_LABEL);
html_source->AddLocalizedString(
"closeFeedbackToastAccessibilityLabel",
IDS_LENS_OVERLAY_CLOSE_FEEDBACK_TOAST_ACCESSIBILITY_LABEL);
const bool dark_mode = lens::LensOverlayShouldUseDarkMode(
ThemeServiceFactory::GetForProfile(Profile::FromWebUI(web_ui)));
// Add finch flags
html_source->AddString(
"resultsLoadingUrl",
lens::features::GetLensOverlayResultsSearchLoadingURL(dark_mode));
html_source->AddBoolean("darkMode", dark_mode);
html_source->AddBoolean("enableErrorPage",
lens::features::GetLensOverlayEnableErrorPage());
html_source->AddBoolean(
"enableGhostLoader",
lens::features::EnableContextualSearchboxGhostLoader());
html_source->AddBoolean(
"showContextualSearchboxLoadingState",
lens::features::ShowContextualSearchboxGhostLoaderLoadingState());
html_source->AddLocalizedString("searchBoxHintContextualDefault",
IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_CONTEXTUAL);
html_source->AddLocalizedString(
"searchBoxHintContextualPdf",
IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_CONTEXTUAL_PDF);
html_source->AddBoolean(
"newFeedbackEnabled",
lens::features::IsLensSearchSidePanelNewFeedbackEnabled());
html_source->AddInteger(
"updatedFeedbackToastTimeoutMs",
lens::features::GetLensUpdatedFeedbackToastTimeoutMs());
html_source->AddString("resultsSearchURL",
lens::features::GetLensOverlayResultsSearchURL());
html_source->AddBoolean(
"enableCloseButtonTweaks",
lens::features::GetVisualSelectionUpdatesEnableCloseButtonTweaks());
html_source->AddBoolean(
"enableSummarizeSuggestionHint",
lens::features::ShouldEnableSummarizeHintForContextualSuggest());
html_source->AddBoolean(
"enableWebviewResults",
lens::features::IsLensSidePanelWebviewResultsEnabled());
html_source->AddBoolean("enableLensAimSuggestions",
lens::features::GetAimSuggestionsEnabled());
// Aim M3 flags
const bool aim_enabled = lens::IsAimM3Enabled(Profile::FromWebUI(web_ui));
html_source->AddBoolean(
"enableFloatingGForHeader",
aim_enabled && lens::features::GetEnableFloatingGForHeader());
html_source->AddBoolean(
"enableClientSideAimHeader",
aim_enabled && lens::features::GetEnableClientSideHeader());
html_source->AddBoolean(
"enableAimSearchbox",
aim_enabled && lens::features::GetAimSearchboxEnabled());
html_source->AddBoolean("showLensButton",
lens::features::GetEnableLensButtonInSearchbox());
html_source->AddBoolean("updatedFeedbackEnabled",
aim_enabled &&
lens::features::GetAimSearchboxEnabled() &&
lens::features::IsLensUpdatedFeedbackEnabled());
// Allow FrameSrc from all Google subdomains as redirects can occur.
GURL results_side_panel_url =
GURL(lens::features::GetLensOverlayResultsSearchURL());
std::string frame_src_directive =
base::StrCat({"frame-src https://*.google.com ",
results_side_panel_url.GetWithEmptyPath().spec(), ";"});
html_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::FrameSrc, frame_src_directive);
// Allow ImgSrc and StyleSrc from chrome-untrusted:// paths for searchbox use.
// Allow data URLs to load in WebUI for full page screenshot.
html_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::ImgSrc,
"img-src 'self' chrome-untrusted://resources "
"https://www.gstatic.com data: blob:;");
html_source->OverrideContentSecurityPolicy(
network::mojom::CSPDirectiveName::StyleSrc,
"style-src 'self' chrome-untrusted://resources chrome-untrusted://theme");
// Support no file types.
html_source->AddString("composeboxImageFileTypes", "");
html_source->AddString("composeboxAttachmentFileTypes", "");
html_source->AddInteger("composeboxFileMaxSize", 0);
html_source->AddInteger("composeboxFileMaxCount", 0);
// Disable typed suggest.
html_source->AddBoolean("composeboxShowTypedSuggest", false);
// Enable ZPS if suggestions are enabled.
html_source->AddBoolean("composeboxShowZps",
lens::features::GetAimSuggestionsEnabled());
// Disable image context suggestions.
html_source->AddBoolean("composeboxShowImageSuggest", false);
// Disable context menu and related features.
html_source->AddBoolean("composeboxShowContextMenu", false);
html_source->AddBoolean("composeboxShowContextMenuDescription", true);
// Send event when escape is pressed.
html_source->AddBoolean("composeboxCloseByEscape", true);
html_source->AddBoolean("composeboxShowRecentTabChip", false);
// Enable submit button.
html_source->AddBoolean("composeboxShowSubmit", true);
// Add strings for post message communication with the remote UI.
lens::ClientToAimMessage handshake_ping;
handshake_ping.mutable_handshake_ping()->add_capabilities(
lens::FeatureCapability::DEFAULT);
html_source->AddString("handshakeMessage",
handshake_ping.SerializeAsString());
// Add required resources.
webui::SetupWebUIDataSource(html_source, kLensUntrustedResources,
IDR_LENS_UNTRUSTED_SIDE_PANEL_SIDE_PANEL_HTML);
html_source->AddResourcePaths(kLensSharedResources);
// Add required resources for the searchbox.
SearchboxHandler::SetupWebUIDataSource(html_source,
Profile::FromWebUI(web_ui));
html_source->AddString(
"searchboxDefaultIcon",
lens::features::GetVisualSelectionUpdatesEnableGradientSuperG()
? "//resources/cr_components/searchbox/icons/google_g_gradient.svg"
: dark_mode
? "//resources/cr_components/searchbox/icons/google_g_cr23.svg"
: "//resources/cr_components/searchbox/icons/google_g.svg");
html_source->AddBoolean("reportMetrics", false);
html_source->AddLocalizedString("searchBoxHint",
IDS_GOOGLE_LENS_SEARCH_BOX_EMPTY_HINT);
html_source->AddLocalizedString("searchBoxHintMultimodal",
IDS_GOOGLE_SEARCH_BOX_EMPTY_HINT_MULTIMODAL);
html_source->AddBoolean("isLensSearchbox", true);
html_source->AddBoolean(
"forceHideEllipsis",
lens::features::GetVisualSelectionUpdatesHideCsbEllipsis());
html_source->AddBoolean(
"enableCsbMotionTweaks",
lens::features::GetVisualSelectionUpdatesEnableCsbMotionTweaks());
html_source->AddBoolean(
"enableVisualSelectionUpdates",
lens::features::IsLensOverlayVisualSelectionUpdatesEnabled());
html_source->AddBoolean(
"enableThumbnailSizingTweaks",
lens::features::GetVisualSelectionUpdatesEnableThumbnailSizingTweaks());
html_source->AddString(
"searchboxComposePlaceholder",
l10n_util::GetStringUTF8(IDS_LENS_COMPOSEBOX_HINT_TEXT));
html_source->AddBoolean("composeboxShowPdfUpload", false);
html_source->AddBoolean("composeboxSmartComposeEnabled", false);
html_source->AddBoolean("composeboxShowDeepSearchButton", false);
html_source->AddBoolean("composeboxShowCreateImageButton", false);
html_source->AddBoolean("dragAndDropEnabled", false);
// If the ThemeSource isn't added here, since this WebUI is
// chrome-untrusted, it will be unable to load stylesheets until a new tab
// is opened.
content::URLDataSource::Add(
Profile::FromWebUI(web_ui),
std::make_unique<ThemeSource>(Profile::FromWebUI(web_ui),
/*serve_untrusted=*/true));
}
void LensSidePanelUntrustedUI::BindInterface(
mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandlerFactory>
receiver) {
lens_side_panel_page_factory_receiver_.reset();
lens_side_panel_page_factory_receiver_.Bind(std::move(receiver));
}
void LensSidePanelUntrustedUI::BindInterface(
mojo::PendingReceiver<lens::mojom::LensGhostLoaderPageHandlerFactory>
receiver) {
lens_ghost_loader_page_factory_receiver_.reset();
lens_ghost_loader_page_factory_receiver_.Bind(std::move(receiver));
}
void LensSidePanelUntrustedUI::BindInterface(
mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
web_ui()->GetWebContents(), std::move(receiver));
}
void LensSidePanelUntrustedUI::BindInterface(
mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
LensSearchboxController* controller =
GetLensSearchController().lens_searchbox_controller();
auto handler = std::make_unique<LensSearchboxHandler>(
std::move(receiver), Profile::FromWebUI(web_ui()),
web_ui()->GetWebContents(), /*lens_searchbox_client=*/controller);
controller->SetSidePanelSearchboxHandler(std::move(handler));
}
void LensSidePanelUntrustedUI::BindInterface(
mojo::PendingReceiver<help_bubble::mojom::HelpBubbleHandlerFactory>
receiver) {
if (help_bubble_handler_factory_receiver_.is_bound()) {
help_bubble_handler_factory_receiver_.reset();
}
help_bubble_handler_factory_receiver_.Bind(std::move(receiver));
}
void LensSidePanelUntrustedUI::BindInterface(
mojo::PendingReceiver<composebox::mojom::PageHandlerFactory> receiver) {
composebox_page_handler_factory_receiver_.reset();
composebox_page_handler_factory_receiver_.Bind(std::move(receiver));
}
void LensSidePanelUntrustedUI::CreatePageHandler(
mojo::PendingRemote<composebox::mojom::Page> pending_page,
mojo::PendingReceiver<composebox::mojom::PageHandler> pending_page_handler,
mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
mojo::PendingReceiver<searchbox::mojom::PageHandler>
pending_searchbox_handler) {
DCHECK(pending_page.is_valid());
auto* controller = GetLensSearchController().lens_composebox_controller();
controller->BindComposebox(
std::move(pending_page_handler), std::move(pending_page),
std::move(pending_searchbox_page), std::move(pending_searchbox_handler));
}
LensSearchController& LensSidePanelUntrustedUI::GetLensSearchController() {
LensSearchController* controller =
LensSearchController::FromWebUIWebContents(web_ui()->GetWebContents());
CHECK(controller);
return *controller;
}
void LensSidePanelUntrustedUI::CreateSidePanelPageHandler(
mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandler> receiver,
mojo::PendingRemote<lens::mojom::LensSidePanelPage> page) {
LensSearchController& controller = GetLensSearchController();
// Once the interface is bound, we want to connect this instance with the
// appropriate instance of LensOverlaySidePanelCoordinator.
controller.lens_overlay_side_panel_coordinator()->BindSidePanel(
std::move(receiver), std::move(page));
}
void LensSidePanelUntrustedUI::CreateGhostLoaderPage(
mojo::PendingRemote<lens::mojom::LensGhostLoaderPage> page) {
LensSearchboxController* controller =
GetLensSearchController().lens_searchbox_controller();
// Once the interface is bound, we want to connect this instance with the
// appropriate instance of LensOverlayController.
controller->BindSidePanelGhostLoader(std::move(page));
}
void LensSidePanelUntrustedUI::CreateHelpBubbleHandler(
mojo::PendingRemote<help_bubble::mojom::HelpBubbleClient> client,
mojo::PendingReceiver<help_bubble::mojom::HelpBubbleHandler> handler) {
help_bubble_handler_ = std::make_unique<user_education::HelpBubbleHandler>(
std::move(handler), std::move(client), this,
std::vector<ui::ElementIdentifier>{kLensSidePanelSearchBoxElementId});
}
LensSidePanelUntrustedUI::~LensSidePanelUntrustedUI() = default;
WEB_UI_CONTROLLER_TYPE_IMPL(LensSidePanelUntrustedUI)
} // namespace lens