| // 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 <string> |
| |
| #include "base/base64url.h" |
| #include "base/notreached.h" |
| #include "base/strings/escape.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "chrome/browser/browser_process.h" |
| #include "components/language/core/common/language_util.h" |
| #include "components/lens/lens_features.h" |
| #include "components/lens/lens_url_utils.h" |
| #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| #include "net/base/url_search_params.h" |
| #include "net/base/url_util.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_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/zlib/google/compression_utils.h" |
| #include "url/gurl.h" |
| |
| namespace lens { |
| namespace { |
| |
| // Query parameter for denoting a search companion request. |
| inline constexpr char kChromeSidePanelParameterKey[] = "gsc"; |
| |
| // Query parameter for the search session id. |
| inline constexpr char kSearchSessionIdParameterKey[] = "gsessionid"; |
| |
| // Query parameter for the request id. |
| inline constexpr char kRequestIdParameterKey[] = "vsrid"; |
| |
| // The url query param key for visual input type, used for contextual queries. |
| inline constexpr char kVisualInputTypeQueryParameterKey[] = "vit"; |
| |
| // Query parameter for the mode. |
| inline constexpr char kModeParameterKey[] = "udm"; |
| |
| // Query parameter for the toolbelt mode. |
| inline constexpr char kToolbeltModeParameterKey[] = "tbm"; |
| |
| // Query parameter values for the mode. |
| inline constexpr char kShoppingModeParameterValue[] = "28"; |
| inline constexpr char kUnimodalModeParameterValue[] = "26"; |
| inline constexpr char kMultimodalModeParameterValue[] = "24"; |
| inline constexpr char kAimModeParameterValue[] = "50"; |
| |
| // Query parameter for the language code. |
| inline constexpr char kLanguageCodeParameterKey[] = "hl"; |
| |
| // Query parameter for the lens mode. |
| inline constexpr char kLensModeParameterKey[] = "lns_mode"; |
| inline constexpr char kLensModeParameterTextValue[] = "text"; |
| inline constexpr char kLensModeParameterUnimodalValue[] = "un"; |
| inline constexpr char kLensModeParameterMultimodalValue[] = "mu"; |
| |
| // Parameters to trigger the Translation One-box. |
| inline constexpr char kSrpStickinessSignalKey[] = "stick"; |
| |
| // Query parameter for the invocation source. |
| inline constexpr char kInvocationSourceParameterKey[] = "source"; |
| inline constexpr char kInvocationSourceAppMenu[] = "chrome.cr.menu"; |
| inline constexpr char kInvocationSourcePageSearchContextMenu[] = |
| "chrome.cr.ctxp"; |
| inline constexpr char kInvocationSourceImageSearchContextMenu[] = |
| "chrome.cr.ctxi"; |
| inline constexpr char kInvocationSourceTextSearchContextMenu[] = |
| "chrome.cr.ctxt"; |
| inline constexpr char kInvocationSourceVideoSearchContextMenu[] = |
| "chrome.cr.ctxv"; |
| inline constexpr char kInvocationSourceFindInPage[] = "chrome.cr.find"; |
| inline constexpr char kInvocationSourceToolbarIcon[] = "chrome.cr.tbic"; |
| inline constexpr char kInvocationSourceOmniboxIcon[] = "chrome.cr.obic"; |
| inline constexpr char kInvocationSourceOmniboxPageAction[] = "chrome.cr.obpa"; |
| inline constexpr char kInvocationSourceOmniboxContextualSuggestion[] = |
| "chrome.cr.obcs"; |
| inline constexpr char kInvocationSourceHomeworkActionChip[] = "chrome.cr.hwac"; |
| |
| // The url query param for the viewport width and height. |
| inline constexpr char kViewportWidthQueryParamKey[] = "biw"; |
| inline constexpr char kViewportHeightQueryParamKey[] = "bih"; |
| |
| // Query parameters that can be appended by GWS to the URL after a redirect. |
| inline constexpr char kXSRFTokenQueryParamKey[] = "sxsrf"; |
| inline constexpr char kSecActQueryParamKey[] = "sec_act"; |
| |
| // The list of query parameters to ignore when comparing search URLs. |
| inline constexpr std::string kIgnoredSearchUrlQueryParameters[] = { |
| kViewportWidthQueryParamKey, kViewportHeightQueryParamKey, |
| kXSRFTokenQueryParamKey, kSecActQueryParamKey, |
| kModeParameterKey, kToolbeltModeParameterKey}; |
| |
| // Query parameter for dark mode. |
| inline constexpr char kDarkModeParameterKey[] = "cs"; |
| inline constexpr char kDarkModeParameterLightValue[] = "0"; |
| inline constexpr char kDarkModeParameterDarkValue[] = "1"; |
| |
| // Query parameter for the Lens footprint. |
| inline constexpr char kLensFootprintParameterKey[] = "lns_fp"; |
| inline constexpr char kLensFootprintParameterValue[] = "1"; |
| |
| // Query parameter for the lens surface. |
| inline constexpr char kLensSurfaceParameterKey[] = "lns_surface"; |
| inline constexpr char kLensSurfaceParameterLensOverlayValue[] = "42"; |
| |
| // Url path for redirects from the results base URL. |
| inline constexpr char kUrlRedirectPath[] = "/url"; |
| |
| // Query parameter for the URL to redirect to. |
| inline constexpr char kUrlQueryParameterKey[] = "url"; |
| |
| // 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"; |
| |
| // Query parameter value for client ID sent to translate API for getting |
| // supported translate languages. |
| inline constexpr char kClientIdQueryParameterValue[] = "lens-overlay"; |
| |
| // Query parameter for the query submission time. This should be set to the |
| // time when the query leaves the client and is sent to the server. |
| inline constexpr char kQuerySubmissionTimeQueryParameter[] = "qsubts"; |
| |
| // Query parameter for the client upload processing duration. This is the time |
| // between the user-perceived query submission time and the time when the |
| // search request is made (i.e. qsubts). |
| inline constexpr char kClientUploadDurationQueryParameter[] = "cud"; |
| |
| // Appends the url params from the map to the url. |
| GURL AppendUrlParamsFromMap( |
| const GURL& url_to_modify, |
| std::map<std::string, std::string> additional_params) { |
| GURL url_with_params = GURL(url_to_modify); |
| for (auto const& param : additional_params) { |
| url_with_params = net::AppendOrReplaceQueryParameter( |
| url_with_params, param.first, param.second); |
| } |
| return url_with_params; |
| } |
| |
| std::string CompressAndEncode(const std::string& serialized_proto) { |
| std::string compressed_proto; |
| compression::GzipCompress(serialized_proto, &compressed_proto); |
| std::string stickiness_signal_value; |
| base::Base64UrlEncode(compressed_proto, |
| base::Base64UrlEncodePolicy::OMIT_PADDING, |
| &stickiness_signal_value); |
| return stickiness_signal_value; |
| } |
| |
| std::string GetURLRefWithoutTextFragment(const GURL& url) { |
| std::string url_ref = url.ref(); |
| auto fragment_start = url_ref.find_first_of(":~:"); |
| if (fragment_start != std::string::npos) { |
| url_ref.resize(fragment_start); |
| } |
| return url_ref; |
| } |
| |
| } // namespace |
| |
| void AppendTranslateParamsToMap(std::map<std::string, std::string>& params, |
| const std::string& query, |
| const std::string& content_language) { |
| lens::StickinessSignals stickiness_signals; |
| stickiness_signals.set_id_namespace(lens::StickinessSignals::TRANSLATE_LITE); |
| auto* intent_query = stickiness_signals.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( |
| query); |
| |
| std::string serialized_proto; |
| stickiness_signals.SerializeToString(&serialized_proto); |
| params[kSrpStickinessSignalKey] = CompressAndEncode(serialized_proto); |
| } |
| |
| void AppendStickinessSignalForFormula( |
| std::map<std::string, std::string>& params, |
| const std::string& formula) { |
| lens::StickinessSignals stickiness_signals; |
| stickiness_signals.set_id_namespace(lens::StickinessSignals::EDUCATION_INPUT); |
| stickiness_signals.mutable_education_input_extension() |
| ->mutable_math_solver_query() |
| ->set_math_input_equation(formula); |
| |
| std::string serialized_proto; |
| stickiness_signals.SerializeToString(&serialized_proto); |
| params[kSrpStickinessSignalKey] = CompressAndEncode(serialized_proto); |
| } |
| |
| GURL AppendCommonSearchParametersToURL(const GURL& url_to_modify, |
| bool use_dark_mode) { |
| GURL new_url = url_to_modify; |
| new_url = net::AppendOrReplaceQueryParameter( |
| new_url, kChromeSidePanelParameterKey, |
| lens::features::GetLensOverlayGscQueryParamValue()); |
| new_url = net::AppendOrReplaceQueryParameter( |
| new_url, kLanguageCodeParameterKey, |
| g_browser_process->GetApplicationLocale()); |
| new_url = AppendDarkModeParamToURL(new_url, use_dark_mode); |
| return new_url; |
| } |
| |
| GURL AppendInvocationSourceParamToURL( |
| const GURL& url_to_modify, |
| lens::LensOverlayInvocationSource invocation_source) { |
| std::string param_value = ""; |
| switch (invocation_source) { |
| case lens::LensOverlayInvocationSource::kAppMenu: |
| param_value = kInvocationSourceAppMenu; |
| break; |
| case lens::LensOverlayInvocationSource::kContentAreaContextMenuPage: |
| param_value = kInvocationSourcePageSearchContextMenu; |
| break; |
| case lens::LensOverlayInvocationSource::kContentAreaContextMenuImage: |
| param_value = kInvocationSourceImageSearchContextMenu; |
| break; |
| case lens::LensOverlayInvocationSource::kContentAreaContextMenuText: |
| param_value = kInvocationSourceTextSearchContextMenu; |
| break; |
| case lens::LensOverlayInvocationSource::kContentAreaContextMenuVideo: |
| param_value = kInvocationSourceVideoSearchContextMenu; |
| break; |
| case lens::LensOverlayInvocationSource::kToolbar: |
| param_value = kInvocationSourceToolbarIcon; |
| break; |
| case lens::LensOverlayInvocationSource::kFindInPage: |
| param_value = kInvocationSourceFindInPage; |
| break; |
| case lens::LensOverlayInvocationSource::kOmnibox: |
| param_value = kInvocationSourceOmniboxIcon; |
| break; |
| case lens::LensOverlayInvocationSource::kOmniboxPageAction: |
| param_value = kInvocationSourceOmniboxPageAction; |
| break; |
| case lens::LensOverlayInvocationSource::kOmniboxContextualSuggestion: |
| param_value = kInvocationSourceOmniboxContextualSuggestion; |
| break; |
| case lens::LensOverlayInvocationSource::kHomeworkActionChip: |
| param_value = kInvocationSourceHomeworkActionChip; |
| break; |
| case lens::LensOverlayInvocationSource::kLVFShutterButton: |
| case lens::LensOverlayInvocationSource::kLVFGallery: |
| case lens::LensOverlayInvocationSource::kContextMenu: |
| case lens::LensOverlayInvocationSource::kAIHub: |
| case lens::LensOverlayInvocationSource::kFREPromo: |
| NOTREACHED() << "Invocation source not supported."; |
| } |
| return net::AppendOrReplaceQueryParameter( |
| url_to_modify, kInvocationSourceParameterKey, param_value); |
| } |
| |
| GURL AppendDarkModeParamToURL(const GURL& url_to_modify, bool use_dark_mode) { |
| return net::AppendOrReplaceQueryParameter( |
| url_to_modify, kDarkModeParameterKey, |
| use_dark_mode ? kDarkModeParameterDarkValue |
| : kDarkModeParameterLightValue); |
| } |
| |
| GURL AppendQuerySubmissionTimeAndClientUploadDurationParamToURL( |
| const GURL& url_to_modify, |
| base::Time query_start_time) { |
| GURL new_url = url_to_modify; |
| base::Time query_submission_time = base::Time::Now(); |
| new_url = net::AppendOrReplaceQueryParameter( |
| new_url, kClientUploadDurationQueryParameter, |
| base::NumberToString( |
| (query_submission_time - query_start_time).InMilliseconds())); |
| new_url = net::AppendOrReplaceQueryParameter( |
| new_url, kQuerySubmissionTimeQueryParameter, |
| base::NumberToString( |
| query_submission_time.InMillisecondsSinceUnixEpoch())); |
| return new_url; |
| } |
| |
| GURL BuildTextOnlySearchURL( |
| base::Time query_start_time, |
| const std::string& text_query, |
| std::optional<GURL> page_url, |
| std::optional<std::string> page_title, |
| std::map<std::string, std::string> additional_search_query_params, |
| lens::LensOverlayInvocationSource invocation_source, |
| lens::LensOverlaySelectionType lens_selection_type, |
| bool use_dark_mode) { |
| GURL url_with_query_params = |
| GURL(lens::features::GetLensOverlayResultsSearchURL()); |
| url_with_query_params = AppendInvocationSourceParamToURL( |
| url_with_query_params, invocation_source); |
| url_with_query_params = AppendUrlParamsFromMap( |
| url_with_query_params, additional_search_query_params); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kTextQueryParameterKey, text_query); |
| if (IsLensTextSelectionType(lens_selection_type)) { |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kLensFootprintParameterKey, |
| kLensFootprintParameterValue); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kLensModeParameterKey, |
| kLensModeParameterTextValue); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kLensSurfaceParameterKey, |
| kLensSurfaceParameterLensOverlayValue); |
| } |
| url_with_query_params = |
| AppendCommonSearchParametersToURL(url_with_query_params, use_dark_mode); |
| url_with_query_params = |
| AppendQuerySubmissionTimeAndClientUploadDurationParamToURL( |
| url_with_query_params, query_start_time); |
| return url_with_query_params; |
| } |
| |
| GURL BuildLensSearchURL( |
| base::Time query_start_time, |
| std::optional<std::string> text_query, |
| std::optional<GURL> page_url, |
| std::optional<std::string> page_title, |
| std::unique_ptr<lens::LensOverlayRequestId> request_id, |
| lens::LensOverlayClusterInfo cluster_info, |
| std::map<std::string, std::string> additional_search_query_params, |
| lens::LensOverlayInvocationSource invocation_source, |
| bool use_dark_mode) { |
| GURL url_with_query_params = |
| GURL(lens::features::GetLensOverlayResultsSearchURL()); |
| url_with_query_params = AppendInvocationSourceParamToURL( |
| url_with_query_params, invocation_source); |
| url_with_query_params = AppendUrlParamsFromMap( |
| url_with_query_params, additional_search_query_params); |
| url_with_query_params = |
| AppendCommonSearchParametersToURL(url_with_query_params, use_dark_mode); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kTextQueryParameterKey, |
| text_query.has_value() ? *text_query : ""); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kLensModeParameterKey, |
| text_query.has_value() ? kLensModeParameterMultimodalValue |
| : kLensModeParameterUnimodalValue); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kLensFootprintParameterKey, |
| kLensFootprintParameterValue); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kLensSurfaceParameterKey, |
| kLensSurfaceParameterLensOverlayValue); |
| |
| // The search url should use the search session id from the cluster info. |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kSearchSessionIdParameterKey, |
| cluster_info.search_session_id()); |
| |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kModeParameterKey, |
| text_query.has_value() ? kMultimodalModeParameterValue |
| : kUnimodalModeParameterValue); |
| |
| 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); |
| url_with_query_params = net::AppendOrReplaceQueryParameter( |
| url_with_query_params, kRequestIdParameterKey, encoded_request_id); |
| url_with_query_params = |
| AppendQuerySubmissionTimeAndClientUploadDurationParamToURL( |
| url_with_query_params, query_start_time); |
| return url_with_query_params; |
| } |
| |
| const std::string ExtractTextQueryParameterValue(const GURL& url) { |
| std::string param_value = ""; |
| net::GetValueForKeyInQuery(url, kTextQueryParameterKey, ¶m_value); |
| return param_value; |
| } |
| |
| const std::string ExtractLensModeParameterValue(const GURL& url) { |
| std::string param_value = ""; |
| net::GetValueForKeyInQuery(url, kLensModeParameterKey, ¶m_value); |
| return param_value; |
| } |
| |
| bool AreSearchUrlsEquivalent(const GURL& a, const GURL& b) { |
| // Check urls without query and reference (fragment) for equality first. |
| GURL::Replacements replacements; |
| replacements.ClearRef(); |
| replacements.ClearQuery(); |
| if (a.ReplaceComponents(replacements) != b.ReplaceComponents(replacements)) { |
| return false; |
| } |
| |
| // Now, compare each query param individually to ensure equivalence. Remove |
| // params that should not contribute to differing search results. |
| net::UrlSearchParams a_search_params( |
| lens::RemoveIgnoredSearchURLParameters(a)); |
| net::UrlSearchParams b_search_params( |
| lens::RemoveIgnoredSearchURLParameters(b)); |
| |
| // Sort params so they are in the same order during comparison. |
| a_search_params.Sort(); |
| b_search_params.Sort(); |
| |
| // Check Search Params for equality |
| // All search params, in order, need to have the same keys and the same |
| // values. |
| return a_search_params.params() == b_search_params.params(); |
| } |
| |
| bool HasCommonSearchQueryParameters(const GURL& url) { |
| // Needed to prevent memory leaks even though we do not use the output. |
| std::string temp_output_string; |
| return net::GetValueForKeyInQuery(url, kChromeSidePanelParameterKey, |
| &temp_output_string) && |
| net::GetValueForKeyInQuery(url, kLanguageCodeParameterKey, |
| &temp_output_string) && |
| net::GetValueForKeyInQuery(url, kDarkModeParameterKey, |
| &temp_output_string); |
| } |
| |
| bool IsValidSearchResultsUrl(const GURL& url) { |
| const GURL results_url(lens::features::GetLensOverlayResultsSearchURL()); |
| return url.is_valid() && results_url.SchemeIs(url.scheme()) && |
| results_url.path() == url.path() && |
| net::registry_controlled_domains::SameDomainOrHost( |
| results_url, url, |
| net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
| } |
| |
| bool IsAimQuery(const GURL& url) { |
| std::string param_value; |
| net::GetValueForKeyInQuery(url, kModeParameterKey, ¶m_value); |
| return param_value == kAimModeParameterValue; |
| } |
| |
| bool ShouldOpenSearchURLInNewTab(const GURL& url) { |
| std::string param_value; |
| net::GetValueForKeyInQuery(url, kModeParameterKey, ¶m_value); |
| const bool is_shopping_mode = param_value == kShoppingModeParameterValue; |
| return IsValidSearchResultsUrl(url) && |
| (is_shopping_mode || |
| (IsAimQuery(url) && !lens::features::ShouldShowAimInSidePanel())); |
| } |
| |
| GURL GetSearchResultsUrlFromRedirectUrl(const GURL& url) { |
| const GURL results_url(lens::features::GetLensOverlayResultsSearchURL()); |
| // The URL should always be valid, have the same domain or host, and share the |
| // same scheme as the base search results URL. |
| if (!url.is_valid() || !results_url.SchemeIs(url.scheme()) || |
| !net::registry_controlled_domains::SameDomainOrHost( |
| results_url, url, |
| net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { |
| return GURL(); |
| } |
| |
| // We only allow paths from `/url` if they are redirecting to a search URL. |
| std::string url_redirect_string; |
| if (url.path() == kUrlRedirectPath && |
| net::GetValueForKeyInQuery(url, kUrlQueryParameterKey, |
| &url_redirect_string)) { |
| // The redirecting URL should be relative. Return false if not. |
| GURL url_to_redirect_to = results_url.Resolve(url_redirect_string); |
| if (!url_to_redirect_to.is_empty() && url_to_redirect_to.is_valid() && |
| results_url.path() == url_to_redirect_to.path()) { |
| // Decode the url if needed since it should be encoded. |
| url_to_redirect_to = GURL(base::UnescapeURLComponent( |
| url_to_redirect_to.spec(), base::UnescapeRule::SPACES)); |
| return url_to_redirect_to; |
| } |
| } |
| |
| return GURL(); |
| } |
| |
| GURL RemoveIgnoredSearchURLParameters(const GURL& url) { |
| GURL processed_url = url; |
| for (const std::string& query_param : kIgnoredSearchUrlQueryParameters) { |
| processed_url = net::AppendOrReplaceQueryParameter( |
| processed_url, query_param, std::nullopt); |
| } |
| return processed_url; |
| } |
| |
| GURL RemoveSidePanelURLParameters(const GURL& url) { |
| GURL processed_url = url; |
| processed_url = net::AppendOrReplaceQueryParameter( |
| processed_url, kChromeSidePanelParameterKey, std::nullopt); |
| return processed_url; |
| } |
| |
| GURL GetSidePanelNewTabUrl(const GURL& side_panel_url, std::string vsrid) { |
| if (side_panel_url.is_empty()) { |
| return GURL(); |
| } |
| // Disable open in new tab for contextual queries. |
| std::string param_value; |
| net::GetValueForKeyInQuery(side_panel_url, kVisualInputTypeQueryParameterKey, |
| ¶m_value); |
| if (!param_value.empty()) { |
| return GURL(); |
| } |
| |
| // Each new tab needs its own unique vsrid. |
| return net::AppendOrReplaceQueryParameter(side_panel_url, |
| kRequestIdParameterKey, vsrid); |
| } |
| |
| GURL BuildTranslateLanguagesURL(std::string_view country, |
| std::string_view language) { |
| GURL url = GURL(lens::features::GetLensOverlayTranslateEndpointURL()); |
| url = |
| net::AppendOrReplaceQueryParameter(url, kCountryQueryParameter, country); |
| url = net::AppendOrReplaceQueryParameter(url, kDisplayLanguageQueryParameter, |
| language); |
| url = net::AppendOrReplaceQueryParameter(url, kClientIdQueryParameter, |
| kClientIdQueryParameterValue); |
| return url; |
| } |
| |
| bool IsLensTextSelectionType( |
| lens::LensOverlaySelectionType lens_selection_type) { |
| return lens_selection_type == lens::SELECT_TEXT_HIGHLIGHT || |
| lens_selection_type == lens::SELECT_TRANSLATED_TEXT || |
| lens_selection_type == lens::TRANSLATE_CHIP || |
| lens_selection_type == lens::SYMBOLIC_MATH_OBJECT; |
| } |
| |
| bool URLsMatchWithoutTextFragment(const GURL& first_url, |
| const GURL& second_url) { |
| return first_url.scheme() == second_url.scheme() && |
| first_url.host() == second_url.host() && |
| first_url.path() == second_url.path() && |
| first_url.query() == second_url.query() && |
| GetURLRefWithoutTextFragment(first_url) == |
| GetURLRefWithoutTextFragment(second_url); |
| } |
| |
| GURL AddPDFScrollToParametersToUrl( |
| const GURL& url, |
| const std::vector<std::string>& text_fragments, |
| int pdf_page_number) { |
| std::string ref = base::StringPrintf("page=%d", pdf_page_number); |
| if (!text_fragments.empty()) { |
| base::StringAppendF(&ref, ":~:text=%s", text_fragments[0]); |
| for (size_t i = 1; i < text_fragments.size(); i++) { |
| base::StringAppendF(&ref, "&text=%s", text_fragments[i]); |
| } |
| } |
| |
| return net::AppendOrReplaceRef(url, ref); |
| } |
| |
| } // namespace lens |