blob: 57fefbb4cdd6ef9b46009d26e55a252cd73a7281 [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_overlay_url_builder.h"
#include "base/base64url.h"
#include "base/strings/escape.h"
#include "base/strings/stringprintf.h"
#include "base/test/protobuf_matchers.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "components/lens/lens_features.h"
#include "net/base/url_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/lens_server_proto/lens_overlay_cluster_info.pb.h"
#include "third_party/lens_server_proto/lens_overlay_knowledge_intent_query.pb.h"
#include "third_party/lens_server_proto/lens_overlay_knowledge_query.pb.h"
#include "third_party/lens_server_proto/lens_overlay_request_id.pb.h"
#include "third_party/lens_server_proto/lens_overlay_selection_type.pb.h"
#include "third_party/lens_server_proto/lens_overlay_stickiness_signals.pb.h"
#include "third_party/lens_server_proto/lens_overlay_translate_stickiness_signals.pb.h"
#include "third_party/lens_server_proto/lens_overlay_video_context_input_params.pb.h"
#include "third_party/lens_server_proto/lens_overlay_video_params.pb.h"
#include "third_party/omnibox_proto/search_context.pb.h"
#include "third_party/zlib/google/compression_utils.h"
namespace lens {
constexpr char kResultsSearchBaseUrl[] = "https://www.google.com/search";
constexpr char kResultsRedirectBaseUrl[] = "https://www.google.com/url";
constexpr char kLanguage[] = "en-US";
constexpr char kPageUrl[] = "https://www.google.com";
constexpr char kPageTitle[] = "Page Title";
// Query parameter for the query submission time.
inline constexpr char kQuerySubmissionTimeQueryParameter[] = "qsubts";
// Query parameter for the client upload processing duration.
inline constexpr char kClientUploadDurationQueryParameter[] = "cud";
// The test time.
constexpr base::Time kTestTime = base::Time::FromSecondsSinceUnixEpoch(1000);
// Query parameters to send to translate API for getting supported translate
// languages.
inline constexpr char kCountryQueryParameter[] = "country";
inline constexpr char kDisplayLanguageQueryParameter[] = "display_language";
inline constexpr char kClientIdQueryParameter[] = "client";
class LensOverlayUrlBuilderTest : public testing::Test {
public:
void SetUp() override {
g_browser_process->SetApplicationLocale(kLanguage);
// Set all the feature params here to keep the test consistent if future
// default values are changed.
feature_list_.InitWithFeaturesAndParameters(
{{lens::features::kLensOverlay,
{
{"results-search-url", kResultsSearchBaseUrl},
}},
{lens::features::kLensOverlayContextualSearchbox,
{
{"use-video-context-for-text-only-requests", "true"},
{"use-video-context-for-multimodal-requests", "true"},
}},
{lens::features::kLensOverlayTranslateLanguages, {}},
{lens::features::kLensOverlayUpdatedClientContext, {}}},
/*disabled_features=*/{});
}
std::string EncodeRequestId(lens::LensOverlayRequestId* request_id) {
std::string serialized_request_id;
EXPECT_TRUE(request_id->SerializeToString(&serialized_request_id));
std::string encoded_request_id;
base::Base64UrlEncode(serialized_request_id,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_request_id);
return encoded_request_id;
}
// Checks that the query submission time and client upload duration params are
// present and removes them.
GURL StripQuerySubmissionTimeAndClientUploadDurationParam(const GURL& url) {
GURL new_url = url;
std::string unused_query_submission_time;
bool has_query_submission_time = net::GetValueForKeyInQuery(
url, kQuerySubmissionTimeQueryParameter, &unused_query_submission_time);
EXPECT_TRUE(has_query_submission_time);
new_url = net::AppendOrReplaceQueryParameter(
new_url, kQuerySubmissionTimeQueryParameter, std::nullopt);
std::string unused_client_upload_duration;
bool has_client_upload_duration =
net::GetValueForKeyInQuery(url, kClientUploadDurationQueryParameter,
&unused_client_upload_duration);
EXPECT_TRUE(has_client_upload_duration);
new_url = net::AppendOrReplaceQueryParameter(
new_url, kClientUploadDurationQueryParameter, std::nullopt);
return new_url;
}
protected:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(LensOverlayUrlBuilderTest, AppendTranslateParamsToMap) {
std::string query = "test";
std::map<std::string, std::string> params;
lens::AppendTranslateParamsToMap(params, query, "auto");
lens::StickinessSignals expected_proto;
expected_proto.set_id_namespace(lens::StickinessSignals::TRANSLATE_LITE);
auto* intent_query = expected_proto.mutable_interpretation()
->mutable_message_set_extension()
->mutable_intent_query();
intent_query->set_name("Translate");
intent_query->mutable_signals()
->mutable_translate_stickiness_signals()
->set_translate_suppress_echo_for_sticky(false);
auto* text_argument = intent_query->add_argument();
text_argument->set_name("Text");
text_argument->mutable_value()->mutable_simple_value()->set_string_value(
"test");
std::string compressed_proto;
ASSERT_TRUE(base::Base64UrlDecode(
params["stick"], base::Base64UrlDecodePolicy::DISALLOW_PADDING,
&compressed_proto));
std::string serialized_proto;
ASSERT_TRUE(compression::GzipUncompress(compressed_proto, &serialized_proto));
lens::StickinessSignals stickiness_signals;
ASSERT_TRUE(stickiness_signals.ParseFromString(serialized_proto));
EXPECT_THAT(stickiness_signals, base::test::EqualsProto(expected_proto));
}
TEST_F(LensOverlayUrlBuilderTest, AppendStickinessSignalForFormula) {
std::string query = "(x + 2) / 4 = 4";
std::string formula = "\\frac{x + 2}{4} = 4";
std::map<std::string, std::string> params;
lens::AppendStickinessSignalForFormula(params, formula);
lens::StickinessSignals expected_proto;
expected_proto.set_id_namespace(lens::StickinessSignals::EDUCATION_INPUT);
expected_proto.mutable_education_input_extension()
->mutable_math_solver_query()
->set_math_input_equation(formula);
std::string compressed_proto;
ASSERT_TRUE(base::Base64UrlDecode(
params["stick"], base::Base64UrlDecodePolicy::DISALLOW_PADDING,
&compressed_proto));
std::string serialized_proto;
ASSERT_TRUE(compression::GzipUncompress(compressed_proto, &serialized_proto));
lens::StickinessSignals stickiness_signals;
ASSERT_TRUE(stickiness_signals.ParseFromString(serialized_proto));
EXPECT_THAT(stickiness_signals, base::test::EqualsProto(expected_proto));
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURL) {
std::string text_query = "Apples";
std::map<std::string, std::string> additional_params;
std::string expected_url =
base::StringPrintf("%s?source=chrome.cr.menu&q=%s&gsc=2&hl=%s&cs=0",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage);
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query,
/*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURLForLensTextSelection) {
std::string text_query = "Apples";
std::map<std::string, std::string> additional_params;
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&q=%s&lns_fp=1&lns_mode=text&lns_surface=42&gsc="
"2&hl=%s&cs=0",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage);
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query,
/*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::SELECT_TEXT_HIGHLIGHT,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest,
BuildTextOnlySearchURLWithVideoContextFlagOff) {
feature_list_.Reset();
feature_list_.InitWithFeaturesAndParameters(
{{lens::features::kLensOverlay,
{
{"results-search-url", kResultsSearchBaseUrl},
}},
{lens::features::kLensOverlayContextualSearchbox,
{
{"use-video-context-for-text-only-requests", "false"},
}}},
/*disabled_features=*/{});
std::string text_query = "Apples";
std::map<std::string, std::string> additional_params;
std::string expected_url =
base::StringPrintf("%s?source=chrome.cr.menu&q=%s&gsc=2&hl=%s&cs=0",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage);
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query, std::make_optional<GURL>(kPageUrl),
std::make_optional<std::string>(kPageTitle), additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURLWithPageUrlAndTitle) {
std::string text_query = "Apples";
std::map<std::string, std::string> additional_params;
std::string expected_url =
base::StringPrintf("%s?source=chrome.cr.menu&q=%s&gsc=2&hl=%s&cs=0",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage);
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query, std::make_optional<GURL>(kPageUrl),
std::make_optional<std::string>(kPageTitle), additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURLWithPageUrl) {
std::string text_query = "Apples";
std::map<std::string, std::string> additional_params;
std::string expected_url =
base::StringPrintf("%s?source=chrome.cr.menu&q=%s&gsc=2&hl=%s&cs=0",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage);
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query, std::make_optional<GURL>(kPageUrl),
/*page_title=*/std::nullopt, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURLEmpty) {
std::string text_query = "";
std::map<std::string, std::string> additional_params;
std::string expected_url =
base::StringPrintf("%s?source=chrome.cr.menu&q=&gsc=2&hl=%s&cs=0",
kResultsSearchBaseUrl, kLanguage);
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query,
/*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURLPunctuation) {
std::string text_query = "Red Apples!?#";
std::map<std::string, std::string> additional_params;
std::string escaped_text_query =
base::EscapeQueryParamValue(text_query, /*use_plus=*/true);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&q=%s&gsc=2&hl=%s&cs=0", kResultsSearchBaseUrl,
escaped_text_query.c_str(), kLanguage);
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query,
/*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildTextOnlySearchURLWhitespace) {
std::string text_query = "Red Apples";
std::map<std::string, std::string> additional_params;
std::string escaped_text_query =
base::EscapeQueryParamValue(text_query, /*use_plus=*/true);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&q=%s&gsc=2&hl=%s&cs=0", kResultsSearchBaseUrl,
escaped_text_query.c_str(), kLanguage);
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildTextOnlySearchURL(
kTestTime, text_query,
/*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
lens::LensOverlaySelectionType::UNKNOWN_SELECTION_TYPE,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildLensSearchURLEmptyClusterInfo) {
std::string text_query = "Green Apples";
std::map<std::string, std::string> additional_params;
std::string escaped_text_query =
base::EscapeQueryParamValue(text_query, /*use_plus=*/true);
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
lens::LensOverlayClusterInfo cluster_info;
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&gsc=2&hl=%s&cs=0&q=%s&lns_mode=mu&"
"lns_fp=1&lns_surface=42&gsessionid=&udm=24&vsrid=%s",
kResultsSearchBaseUrl, kLanguage, escaped_text_query.c_str(),
EncodeRequestId(request_id.get()).c_str());
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime, text_query, /*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, std::move(request_id), cluster_info,
additional_params, lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildLensSearchURLWithSessionId) {
std::string text_query = "Green Apples";
std::map<std::string, std::string> additional_params;
std::string escaped_text_query =
base::EscapeQueryParamValue(text_query, /*use_plus=*/true);
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
std::string search_session_id = "search_session_id";
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
lens::LensOverlayClusterInfo cluster_info;
cluster_info.set_search_session_id(search_session_id);
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&gsc=2&hl=%s&cs=0&q=%s&lns_mode=mu&"
"lns_fp=1&lns_surface=42&gsessionid=%s&udm=24&vsrid=%s",
kResultsSearchBaseUrl, kLanguage, escaped_text_query.c_str(),
search_session_id.c_str(), EncodeRequestId(request_id.get()).c_str());
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime, text_query, /*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, std::move(request_id), cluster_info,
additional_params, lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildLensSearchURLWithNoTextQuery) {
std::map<std::string, std::string> additional_params;
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
std::string search_session_id = "search_session_id";
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
lens::LensOverlayClusterInfo cluster_info;
cluster_info.set_search_session_id(search_session_id);
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string serialized_request_id;
CHECK(request_id.get()->SerializeToString(&serialized_request_id));
std::string encoded_request_id;
base::Base64UrlEncode(serialized_request_id,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_request_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&gsc=2&hl=%s&cs=0&q=&lns_mode=un&"
"lns_fp=1&lns_surface=42&gsessionid=%s&udm=26&vsrid=%s",
kResultsSearchBaseUrl, kLanguage, search_session_id.c_str(),
encoded_request_id.c_str());
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime,
/*text_query=*/std::nullopt, /*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, std::move(request_id), cluster_info,
additional_params, lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildLensSearchURLWithAdditionalParams) {
std::map<std::string, std::string> additional_params = {{"param", "value"}};
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
std::string search_session_id = "search_session_id";
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
lens::LensOverlayClusterInfo cluster_info;
cluster_info.set_search_session_id(search_session_id);
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string serialized_request_id;
CHECK(request_id.get()->SerializeToString(&serialized_request_id));
std::string encoded_request_id;
base::Base64UrlEncode(serialized_request_id,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_request_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&param=value&gsc=2&hl=%s&cs=0&q=&lns_"
"mode=un&lns_fp=1&lns_surface=42&gsessionid=%s&udm=26&"
"vsrid=%s",
kResultsSearchBaseUrl, kLanguage, search_session_id.c_str(),
encoded_request_id.c_str());
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime,
/*text_query=*/std::nullopt, /*page_url=*/std::nullopt,
/*page_title=*/std::nullopt, std::move(request_id), cluster_info,
additional_params, lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, BuildMultimodalSearchURLWithVideoContext) {
std::string text_query = "Green Apples";
std::map<std::string, std::string> additional_params;
std::string escaped_text_query =
base::EscapeQueryParamValue(text_query, /*use_plus=*/true);
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
std::string search_session_id = "search_session_id";
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
lens::LensOverlayClusterInfo cluster_info;
cluster_info.set_search_session_id(search_session_id);
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string serialized_request_id;
CHECK(request_id.get()->SerializeToString(&serialized_request_id));
std::string encoded_request_id;
base::Base64UrlEncode(serialized_request_id,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_request_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&gsc=2&hl=%s&cs=0&q=%s&lns_"
"mode=mu&lns_fp=1&lns_surface=42&gsessionid=%s&udm=24&"
"vsrid=%s",
kResultsSearchBaseUrl, kLanguage, escaped_text_query.c_str(),
search_session_id.c_str(), encoded_request_id.c_str());
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime, text_query, std::make_optional<GURL>(kPageUrl),
std::make_optional<std::string>(kPageTitle),
std::move(request_id), cluster_info, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest,
BuildImageOnlySearchURLWithVideoContextDoesNotAttachContext) {
std::map<std::string, std::string> additional_params;
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
std::string search_session_id = "search_session_id";
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
lens::LensOverlayClusterInfo cluster_info;
cluster_info.set_search_session_id(search_session_id);
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string serialized_request_id;
CHECK(request_id.get()->SerializeToString(&serialized_request_id));
std::string encoded_request_id;
base::Base64UrlEncode(serialized_request_id,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_request_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&gsc=2&hl=%s&cs=0&q=&lns_"
"mode=un&lns_fp=1&lns_surface=42&gsessionid=%s&udm=26&"
"vsrid=%s",
kResultsSearchBaseUrl, kLanguage, search_session_id.c_str(),
encoded_request_id.c_str());
EXPECT_EQ(
StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime,
/*text_query=*/std::nullopt, std::make_optional<GURL>(kPageUrl),
std::make_optional<std::string>(kPageTitle),
std::move(request_id), cluster_info, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest,
BuildMultimodalSearchURLWithVideoContextFlagOff) {
feature_list_.Reset();
feature_list_.InitWithFeaturesAndParameters(
{{lens::features::kLensOverlay,
{
{"results-search-url", kResultsSearchBaseUrl},
}},
{lens::features::kLensOverlayContextualSearchbox,
{
{"use-video-context-for-multimodal-requests", "false"},
}},
{lens::features::kLensOverlayUpdatedClientContext, {}}},
/*disabled_features=*/{});
std::string text_query = "Green Apples";
std::map<std::string, std::string> additional_params;
std::string escaped_text_query =
base::EscapeQueryParamValue(text_query, /*use_plus=*/true);
uint64_t uuid = 12345;
int sequence_id = 1;
int image_sequence_id = 3;
std::string search_session_id = "search_session_id";
std::unique_ptr<lens::LensOverlayRequestId> request_id =
std::make_unique<lens::LensOverlayRequestId>();
lens::LensOverlayClusterInfo cluster_info;
cluster_info.set_search_session_id(search_session_id);
request_id->set_uuid(uuid);
request_id->set_sequence_id(sequence_id);
request_id->set_image_sequence_id(image_sequence_id);
std::string serialized_request_id;
CHECK(request_id.get()->SerializeToString(&serialized_request_id));
std::string encoded_request_id;
base::Base64UrlEncode(serialized_request_id,
base::Base64UrlEncodePolicy::OMIT_PADDING,
&encoded_request_id);
std::string expected_url = base::StringPrintf(
"%s?source=chrome.cr.menu&gsc=2&hl=%s&cs=0&q=%s&lns_"
"mode=mu&lns_fp=1&lns_surface=42&gsessionid=%s&udm=24&"
"vsrid=%s",
kResultsSearchBaseUrl, kLanguage, escaped_text_query.c_str(),
search_session_id.c_str(), encoded_request_id.c_str());
EXPECT_EQ(StripQuerySubmissionTimeAndClientUploadDurationParam(
lens::BuildLensSearchURL(
kTestTime, text_query, std::make_optional<GURL>(kPageUrl),
std::make_optional<std::string>(kPageTitle),
std::move(request_id), cluster_info, additional_params,
lens::LensOverlayInvocationSource::kAppMenu,
/*use_dark_mode=*/false)),
expected_url);
}
TEST_F(LensOverlayUrlBuilderTest, HasCommonSearchQueryParameters) {
const GURL url(base::StringPrintf("%s?gsc=2&hl=%s&cs=1",
kResultsSearchBaseUrl, kLanguage));
EXPECT_TRUE(lens::HasCommonSearchQueryParameters(url));
}
TEST_F(LensOverlayUrlBuilderTest, HasCommonSearchQueryParametersWithoutLocale) {
const GURL url(base::StringPrintf("%s?gsc=2", kResultsSearchBaseUrl));
EXPECT_FALSE(lens::HasCommonSearchQueryParameters(url));
}
TEST_F(LensOverlayUrlBuilderTest,
HasCommonSearchQueryParametersMissingQueryParams) {
const GURL failing_url1(
base::StringPrintf("%s?gsc=2", kResultsSearchBaseUrl));
EXPECT_FALSE(lens::HasCommonSearchQueryParameters(failing_url1));
const GURL failing_url2(
base::StringPrintf("%s?hl=%s", kResultsSearchBaseUrl, kLanguage));
EXPECT_FALSE(lens::HasCommonSearchQueryParameters(failing_url2));
const GURL failing_url3(base::StringPrintf("%s?cs=1", kResultsSearchBaseUrl));
EXPECT_FALSE(lens::HasCommonSearchQueryParameters(failing_url3));
}
TEST_F(LensOverlayUrlBuilderTest,
AppendInvocationSourceParamToUrlAppendsEntryPoints) {
const GURL base_url(kResultsSearchBaseUrl);
std::string expected_app_menu_url =
base::StringPrintf("%s?source=chrome.cr.menu", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendInvocationSourceParamToURL(
base_url, lens::LensOverlayInvocationSource::kAppMenu),
expected_app_menu_url);
std::string expected_context_menu_page_url =
base::StringPrintf("%s?source=chrome.cr.ctxp", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendInvocationSourceParamToURL(
base_url,
lens::LensOverlayInvocationSource::kContentAreaContextMenuPage),
expected_context_menu_page_url);
std::string expected_context_menu_image_url =
base::StringPrintf("%s?source=chrome.cr.ctxi", kResultsSearchBaseUrl);
EXPECT_EQ(
lens::AppendInvocationSourceParamToURL(
base_url,
lens::LensOverlayInvocationSource::kContentAreaContextMenuImage),
expected_context_menu_image_url);
std::string expected_toolbar_url =
base::StringPrintf("%s?source=chrome.cr.tbic", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendInvocationSourceParamToURL(
base_url, lens::LensOverlayInvocationSource::kToolbar),
expected_toolbar_url);
std::string expected_find_in_page_url =
base::StringPrintf("%s?source=chrome.cr.find", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendInvocationSourceParamToURL(
base_url, lens::LensOverlayInvocationSource::kFindInPage),
expected_find_in_page_url);
std::string expected_omnibox_url =
base::StringPrintf("%s?source=chrome.cr.obic", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendInvocationSourceParamToURL(
base_url, lens::LensOverlayInvocationSource::kOmnibox),
expected_omnibox_url);
std::string expected_context_menu_video_url =
base::StringPrintf("%s?source=chrome.cr.ctxv", kResultsSearchBaseUrl);
EXPECT_EQ(
lens::AppendInvocationSourceParamToURL(
base_url,
lens::LensOverlayInvocationSource::kContentAreaContextMenuVideo),
expected_context_menu_video_url);
}
TEST_F(LensOverlayUrlBuilderTest,
AppendDarkModeParamToURLToUrlAppendsDarkMode) {
const GURL base_url(kResultsSearchBaseUrl);
std::string expected_light_mode_url =
base::StringPrintf("%s?cs=0", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendDarkModeParamToURL(base_url, /*use_dark_mode=*/false),
expected_light_mode_url);
std::string expected_dark_mode_url =
base::StringPrintf("%s?cs=1", kResultsSearchBaseUrl);
EXPECT_EQ(lens::AppendDarkModeParamToURL(base_url, /*use_dark_mode=*/true),
expected_dark_mode_url);
}
TEST_F(LensOverlayUrlBuilderTest, HasCommonSearchQueryParametersNoQueryParams) {
const GURL url(kResultsSearchBaseUrl);
EXPECT_FALSE(lens::HasCommonSearchQueryParameters(url));
}
TEST_F(LensOverlayUrlBuilderTest, IsValidSearchURL) {
EXPECT_TRUE(lens::IsValidSearchResultsUrl(GURL(kResultsSearchBaseUrl)));
}
TEST_F(LensOverlayUrlBuilderTest, IsValidSearchURLDifferentDomains) {
EXPECT_FALSE(
lens::IsValidSearchResultsUrl(GURL("https://test.google/search")));
}
TEST_F(LensOverlayUrlBuilderTest, IsValidSearchURLDifferentSchemes) {
// GetContent() should return everything after the scheme.
GURL different_scheme_url = GURL(base::StringPrintf(
"chrome://%s", GURL(kResultsSearchBaseUrl).GetContent().c_str()));
EXPECT_FALSE(lens::IsValidSearchResultsUrl(different_scheme_url));
}
TEST_F(LensOverlayUrlBuilderTest, IsValidSearchURLDifferentPaths) {
EXPECT_FALSE(lens::IsValidSearchResultsUrl(
GURL(kResultsSearchBaseUrl).GetWithEmptyPath()));
}
TEST_F(LensOverlayUrlBuilderTest, IsValidSearchURLInvalidURL) {
EXPECT_FALSE(lens::IsValidSearchResultsUrl(GURL()));
}
TEST_F(LensOverlayUrlBuilderTest, RemoveIgnoredSearchURLParameters) {
std::string text_query = "Apples";
std::string viewport_width = "400";
std::string viewport_height = "500";
std::string initial_url = base::StringPrintf(
"%s?q=%s&gsc=2&hl=%s&biw=%s&bih=%s&sec_act=1&sxsrf=token",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage,
viewport_width.c_str(), viewport_height.c_str());
std::string expected_url =
base::StringPrintf("%s?q=%s&gsc=2&hl=%s", kResultsSearchBaseUrl,
text_query.c_str(), kLanguage);
EXPECT_EQ(lens::RemoveIgnoredSearchURLParameters(GURL(initial_url)),
GURL(expected_url));
}
TEST_F(LensOverlayUrlBuilderTest, RemoveSidePanelURLParameters) {
std::string text_query = "Apples";
std::string viewport_width = "400";
std::string viewport_height = "500";
std::string initial_url = base::StringPrintf(
"%s?q=%s&gsc=2&hl=%s&biw=%s&bih=%s&sec_act=1&sxsrf=token",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage,
viewport_width.c_str(), viewport_height.c_str());
std::string expected_url =
base::StringPrintf("%s?q=%s&hl=%s&biw=%s&bih=%s&sec_act=1&sxsrf=token",
kResultsSearchBaseUrl, text_query.c_str(), kLanguage,
viewport_width.c_str(), viewport_height.c_str());
EXPECT_EQ(lens::RemoveSidePanelURLParameters(GURL(initial_url)),
GURL(expected_url));
}
TEST_F(LensOverlayUrlBuilderTest, GetSearchResultsUrlFromRedirectUrl) {
std::string text_query = "Apples";
std::string viewport_width = "400";
std::string viewport_height = "500";
std::string relative_search_url = base::StringPrintf(
"/search?q=%s&gsc=2&hl=%s&biw=%s&bih=%s", text_query.c_str(), kLanguage,
viewport_width.c_str(), viewport_height.c_str());
std::string escaped_relative_search_url =
base::EscapeUrlEncodedData(relative_search_url, /*use_plus=*/false);
GURL search_url = GURL(kResultsSearchBaseUrl).Resolve(relative_search_url);
std::string initial_url =
base::StringPrintf("%s?url=%s", kResultsRedirectBaseUrl,
escaped_relative_search_url.c_str());
EXPECT_EQ(lens::GetSearchResultsUrlFromRedirectUrl(GURL(initial_url)),
GURL(search_url));
}
TEST_F(LensOverlayUrlBuilderTest, IsLensTextSelectionType) {
EXPECT_TRUE(IsLensTextSelectionType(lens::SELECT_TEXT_HIGHLIGHT));
EXPECT_TRUE(IsLensTextSelectionType(lens::SELECT_TRANSLATED_TEXT));
EXPECT_TRUE(IsLensTextSelectionType(lens::TRANSLATE_CHIP));
EXPECT_FALSE(IsLensTextSelectionType(lens::REGION_SEARCH));
EXPECT_FALSE(IsLensTextSelectionType(lens::TAP_ON_OBJECT));
EXPECT_FALSE(IsLensTextSelectionType(lens::MULTIMODAL_SEARCH));
}
TEST_F(LensOverlayUrlBuilderTest, BuildTranslateLanguagesURL) {
GURL endpoint_url = GURL(features::GetLensOverlayTranslateEndpointURL());
GURL translate_url_english_us = BuildTranslateLanguagesURL("US", "en");
GURL translate_url_english_uk = BuildTranslateLanguagesURL("UK", "en");
GURL translate_url_french_nocountry = BuildTranslateLanguagesURL("", "fr");
EXPECT_EQ(translate_url_english_us.GetWithEmptyPath(),
endpoint_url.GetWithEmptyPath());
EXPECT_EQ(translate_url_english_uk.GetWithEmptyPath(),
endpoint_url.GetWithEmptyPath());
EXPECT_EQ(translate_url_french_nocountry.GetWithEmptyPath(),
endpoint_url.GetWithEmptyPath());
std::string country_param_value;
net::GetValueForKeyInQuery(translate_url_english_us, kCountryQueryParameter,
&country_param_value);
EXPECT_EQ(country_param_value, "US");
net::GetValueForKeyInQuery(translate_url_english_uk, kCountryQueryParameter,
&country_param_value);
EXPECT_EQ(country_param_value, "UK");
net::GetValueForKeyInQuery(translate_url_french_nocountry,
kCountryQueryParameter, &country_param_value);
EXPECT_EQ(country_param_value, "");
std::string lang_param_value;
net::GetValueForKeyInQuery(translate_url_english_us,
kDisplayLanguageQueryParameter, &lang_param_value);
EXPECT_EQ(lang_param_value, "en");
net::GetValueForKeyInQuery(translate_url_french_nocountry,
kDisplayLanguageQueryParameter, &lang_param_value);
EXPECT_EQ(lang_param_value, "fr");
net::GetValueForKeyInQuery(translate_url_english_uk,
kDisplayLanguageQueryParameter, &lang_param_value);
EXPECT_EQ(lang_param_value, "en");
std::string client_param_value;
net::GetValueForKeyInQuery(translate_url_english_us, kClientIdQueryParameter,
&client_param_value);
EXPECT_EQ(client_param_value, "lens-overlay");
client_param_value = "";
net::GetValueForKeyInQuery(translate_url_french_nocountry,
kClientIdQueryParameter, &client_param_value);
EXPECT_EQ(client_param_value, "lens-overlay");
client_param_value = "";
net::GetValueForKeyInQuery(translate_url_english_uk, kClientIdQueryParameter,
&client_param_value);
EXPECT_EQ(client_param_value, "lens-overlay");
}
TEST_F(LensOverlayUrlBuilderTest, ShouldOpenSearchURLInNewTab) {
const GURL base_results_url = GURL(kResultsSearchBaseUrl);
const GURL results_url_shopping_mode =
GURL(std::string(kResultsSearchBaseUrl) + "?udm=28");
const GURL results_url_unimodal =
GURL(std::string(kResultsSearchBaseUrl) + "?udm=26");
const GURL results_url_multimodal =
GURL(std::string(kResultsSearchBaseUrl) + "?udm=24");
const GURL results_url_aim_mode =
GURL(std::string(kResultsSearchBaseUrl) + "?udm=50");
EXPECT_FALSE(lens::ShouldOpenSearchURLInNewTab(
base_results_url, /*is_aim_feature_enabled=*/false));
EXPECT_FALSE(lens::ShouldOpenSearchURLInNewTab(
results_url_unimodal, /*is_aim_feature_enabled=*/false));
EXPECT_FALSE(lens::ShouldOpenSearchURLInNewTab(
results_url_multimodal, /*is_aim_feature_enabled=*/false));
EXPECT_TRUE(lens::ShouldOpenSearchURLInNewTab(
results_url_shopping_mode, /*is_aim_feature_enabled=*/false));
EXPECT_TRUE(lens::ShouldOpenSearchURLInNewTab(
results_url_aim_mode, /*is_aim_feature_enabled=*/false));
EXPECT_FALSE(lens::ShouldOpenSearchURLInNewTab(
results_url_aim_mode, /*is_aim_feature_enabled=*/true));
}
TEST_F(LensOverlayUrlBuilderTest, URLsMatchWithoutTextFragment) {
// Text fragments do not match.
EXPECT_TRUE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#ref"),
GURL("https://www.google.com/path?q=text#ref:~:text=apples")));
EXPECT_TRUE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#ref:~:text=oranges"),
GURL("https://www.google.com/path?q=text#ref")));
EXPECT_TRUE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#ref:~:text=oranges"),
GURL("https://www.google.com/path?q=text#ref:~:text=apples")));
// Refs without text fragments do not match.
EXPECT_FALSE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#section1"),
GURL("https://www.google.com/path?q=text#section2")));
EXPECT_FALSE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#section1:~:text=oranges"),
GURL("https://www.google.com/path?q=text#section2:~:text=apples")));
// Query param does not match.
EXPECT_FALSE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#ref"),
GURL("https://www.google.com/path?q=query#ref")));
// Path does not match.
EXPECT_FALSE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/search?q=text#ref"),
GURL("https://www.google.com/path?q=text#ref")));
// Host does not match.
EXPECT_FALSE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#ref"),
GURL("https://www.example.com/path?q=text#ref")));
// Scheme does not match.
EXPECT_FALSE(URLsMatchWithoutTextFragment(
GURL("https://www.google.com/path?q=text#ref"),
GURL("http://www.google.com/path?q=text#ref")));
}
TEST_F(LensOverlayUrlBuilderTest, AddPDFScrollToParametersToUrl) {
const GURL base_url = GURL(
"https://www.example.com/"
"example.pdf?query=example#anycurrenthash:~:text=example");
const int expected_pdf_page_number = 19;
const std::vector<std::string> expected_text_fragments = {"apples", "oranges",
"pineapples"};
const GURL actual_url = AddPDFScrollToParametersToUrl(
base_url, expected_text_fragments, expected_pdf_page_number);
EXPECT_TRUE(base_url.EqualsIgnoringRef(actual_url));
EXPECT_EQ(actual_url.GetRef(),
base::StringPrintf("page=%d:~:text=%s&text=%s&text=%s",
expected_pdf_page_number, "apples", "oranges",
"pineapples"));
const std::vector<std::string> expected_one_fragment = {"apples"};
const GURL actual_url_one_fragment = AddPDFScrollToParametersToUrl(
base_url, expected_one_fragment, expected_pdf_page_number);
EXPECT_TRUE(base_url.EqualsIgnoringRef(actual_url_one_fragment));
EXPECT_EQ(actual_url_one_fragment.GetRef(),
base::StringPrintf("page=%d:~:text=%s", expected_pdf_page_number,
"apples"));
const GURL actual_url_no_fragments =
AddPDFScrollToParametersToUrl(base_url, {}, expected_pdf_page_number);
EXPECT_TRUE(base_url.EqualsIgnoringRef(actual_url_no_fragments));
EXPECT_EQ(actual_url_no_fragments.GetRef(),
base::StringPrintf("page=%d", expected_pdf_page_number));
}
TEST_F(LensOverlayUrlBuilderTest, ExtractTimeInSecondsFromQueryIfExists) {
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=10s")),
base::Seconds(10));
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=10")),
base::Seconds(10));
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=1s")),
base::Seconds(1));
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=1")),
base::Seconds(1));
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=60s")),
base::Seconds(60));
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test")),
std::nullopt);
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=s")),
std::nullopt);
EXPECT_EQ(lens::ExtractTimeInSecondsFromQueryIfExists(
GURL("https://www.youtube.com/watch?v=test&t=abcs")),
std::nullopt);
}
TEST_F(LensOverlayUrlBuilderTest, ExtractVideoNameIfExists) {
EXPECT_EQ(lens::ExtractVideoNameIfExists(
GURL("https://www.youtube.com/watch?v=VIDEO_ID")),
"VIDEO_ID");
EXPECT_EQ(lens::ExtractVideoNameIfExists(
GURL("https://www.youtube.com/embed/VIDEO_ID")),
"VIDEO_ID");
EXPECT_EQ(lens::ExtractVideoNameIfExists(GURL("https://www.google.com")),
std::nullopt);
EXPECT_EQ(
lens::ExtractVideoNameIfExists(GURL("https://www.google.com?v=VIDEO_ID")),
std::nullopt);
EXPECT_EQ(lens::ExtractVideoNameIfExists(
GURL("https://www.google.com/embed/VIDEO_ID")),
std::nullopt);
EXPECT_EQ(lens::ExtractVideoNameIfExists(
GURL("https://www.google.com/watch?v=VIDEO_ID")),
std::nullopt);
EXPECT_EQ(
lens::ExtractVideoNameIfExists(GURL("https://www.youtube.com/watch?v=")),
std::nullopt);
EXPECT_EQ(
lens::ExtractVideoNameIfExists(GURL("https://www.youtube.com/embed/")),
std::nullopt);
}
} // namespace lens