blob: df3dc6b5aab756ca9b5d00dcaa03a0ce91e02b25 [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/webui/searchbox/lens_searchbox_handler.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/omnibox/omnibox_controller.h"
#include "chrome/browser/ui/webui/searchbox/lens_searchbox_client.h"
#include "chrome/browser/ui/webui/searchbox/searchbox_omnibox_client.h"
#include "components/lens/lens_features.h"
#include "components/lens/proto/server/lens_overlay_response.pb.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/omnibox_client.h"
#include "components/omnibox/browser/vector_icons.h"
#include "components/prefs/pref_service.h"
#include "components/sessions/core/session_id.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "url/gurl.h"
namespace {
// Interface that allows the Lens searchbox to interact with its embedder
// (i.e., LensSearchboxController).
class LensOmniboxClient : public SearchboxOmniboxClient {
public:
LensOmniboxClient(Profile* profile,
content::WebContents* web_contents,
LensSearchboxClient* lens_searchbox_client);
~LensOmniboxClient() override;
// OmniboxClient:
SessionID GetSessionID() const override;
const GURL& GetURL() const override;
metrics::OmniboxEventProto::PageClassification GetPageClassification(
bool is_prefetch) const override;
std::optional<lens::proto::LensOverlaySuggestInputs>
GetLensOverlaySuggestInputs() const override;
void OnThumbnailRemoved() override;
void OnAutocompleteAccept(
const GURL& destination_url,
TemplateURLRef::PostContent* post_content,
WindowOpenDisposition disposition,
ui::PageTransition transition,
AutocompleteMatchType::Type match_type,
base::TimeTicks match_selection_timestamp,
bool destination_url_entered_without_scheme,
bool destination_url_entered_with_http_scheme,
const std::u16string& text,
const AutocompleteMatch& match,
const AutocompleteMatch& alternative_nav_match) override;
private:
raw_ptr<LensSearchboxClient> lens_searchbox_client_;
};
LensOmniboxClient::LensOmniboxClient(Profile* profile,
content::WebContents* web_contents,
LensSearchboxClient* lens_searchbox_client)
: SearchboxOmniboxClient(profile, web_contents),
lens_searchbox_client_(lens_searchbox_client) {}
LensOmniboxClient::~LensOmniboxClient() = default;
SessionID LensOmniboxClient::GetSessionID() const {
return lens_searchbox_client_->GetTabId();
}
const GURL& LensOmniboxClient::GetURL() const {
return lens_searchbox_client_->GetPageURL();
}
std::optional<lens::proto::LensOverlaySuggestInputs>
LensOmniboxClient::GetLensOverlaySuggestInputs() const {
return lens_searchbox_client_->GetLensSuggestInputs();
}
void LensOmniboxClient::OnThumbnailRemoved() {
lens_searchbox_client_->OnThumbnailRemoved();
}
metrics::OmniboxEventProto::PageClassification
LensOmniboxClient::GetPageClassification(bool is_prefetch) const {
return lens_searchbox_client_->GetPageClassification();
}
void LensOmniboxClient::OnAutocompleteAccept(
const GURL& destination_url,
TemplateURLRef::PostContent* post_content,
WindowOpenDisposition disposition,
ui::PageTransition transition,
AutocompleteMatchType::Type match_type,
base::TimeTicks match_selection_timestamp,
bool destination_url_entered_without_scheme,
bool destination_url_entered_with_http_scheme,
const std::u16string& text,
const AutocompleteMatch& match,
const AutocompleteMatch& alternative_nav_match) {
lens_searchbox_client_->OnSuggestionAccepted(
destination_url, match.type,
match.subtypes.contains(omnibox::SUBTYPE_ZERO_PREFIX));
}
} // namespace
LensSearchboxHandler::LensSearchboxHandler(
mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
Profile* profile,
content::WebContents* web_contents,
LensSearchboxClient* lens_searchbox_client)
: SearchboxHandler(
std::move(pending_page_handler),
profile,
web_contents,
std::make_unique<OmniboxController>(
std::make_unique<LensOmniboxClient>(profile,
web_contents,
lens_searchbox_client),
lens::features::GetLensSearchboxAutocompleteTimeout())),
lens_searchbox_client_(lens_searchbox_client) {
autocomplete_controller_observation_.Observe(autocomplete_controller());
}
LensSearchboxHandler::~LensSearchboxHandler() = default;
std::string LensSearchboxHandler::AutocompleteIconToResourceName(
const gfx::VectorIcon& icon) const {
// The default icon for contextual suggestions is the subdirectory arrow right
// icon. For the Lens searchbox, we want to stay consistent with the search
// loupe instead.
if (icon.name == omnibox::kSubdirectoryArrowRightIcon.name) {
return searchbox_internal::kSearchIconResourceName;
}
return SearchboxHandler::AutocompleteIconToResourceName(icon);
}
void LensSearchboxHandler::SetPage(
mojo::PendingRemote<searchbox::mojom::Page> pending_page) {
SearchboxHandler::SetPage(std::move(pending_page));
// The client may have text waiting to be sent to the searchbox that it
// couldn't do earlier since the page binding was not set. So now we let the
// client know the binding is ready.
lens_searchbox_client_->OnPageBound();
}
void LensSearchboxHandler::OnFocusChanged(bool focused) {
SearchboxHandler::OnFocusChanged(focused);
lens_searchbox_client_->OnFocusChanged(focused);
}
void LensSearchboxHandler::QueryAutocomplete(const std::u16string& input,
bool prevent_inline_autocomplete) {
lens_searchbox_client_->OnTextModified();
SearchboxHandler::QueryAutocomplete(input, prevent_inline_autocomplete);
}
void LensSearchboxHandler::SetInputText(const std::string& input_text) {
page_->SetInputText(input_text);
}
void LensSearchboxHandler::SetThumbnail(const std::string& thumbnail_url,
bool is_deletable) {
page_->SetThumbnail(thumbnail_url, is_deletable);
}
void LensSearchboxHandler::OnThumbnailRemoved() {
omnibox_controller()->client()->OnThumbnailRemoved();
}
void LensSearchboxHandler::OnAutocompleteStopTimerTriggered(
const AutocompleteInput& input) {
// Only notify the lens controller when autocomplete stop timer is triggered
// for zero suggest inputs.
if (input.IsZeroSuggest() && autocomplete_controller()->done()) {
lens_searchbox_client_->ShowGhostLoaderErrorState();
}
}
void LensSearchboxHandler::OnResultChanged(AutocompleteController* controller,
bool default_match_changed) {
SearchboxHandler::OnResultChanged(controller, default_match_changed);
// Show the ghost loader error state if the result is empty on the last
// async pass of the autocomplete controller (there will not be anymore
// updates). controller->done() itself is not a sufficient check since it
// takes into account kStop update types which occurs when a user unfocuses
// the searchbox, and the error state should not be shown in this case.
if (controller->done() &&
controller->last_update_type() ==
AutocompleteController::UpdateType::kLastAsyncPass &&
controller->result().empty()) {
lens_searchbox_client_->ShowGhostLoaderErrorState();
}
if (controller->input().IsZeroSuggest() && !controller->result().empty()) {
lens_searchbox_client_->OnZeroSuggestShown();
}
}