File: lens_overlay_url_builder.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (586 lines) | stat: -rw-r--r-- 24,686 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
// 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 "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 the search text query.
inline constexpr char kTextQueryParameterKey[] = "q";

// 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 kMGTModeParameterValue[] = "50";

// Query parameter for the language code.
inline constexpr char kLanguageCodeParameterKey[] = "hl";

// Query parameter for the video context.
inline constexpr char kVideoContextParameterKey[] = "vidcip";

// 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 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 AppendVideoContextParamToURL(const GURL& url_to_modify,
                                  std::optional<GURL> page_url) {
  if (!page_url.has_value()) {
    return url_to_modify;
  }

  lens::LensOverlayVideoParams video_params;
  video_params.mutable_video_context_input_params()->set_url(page_url->spec());
  std::string serialized_video_params;
  if (!video_params.SerializeToString(&serialized_video_params)) {
    return url_to_modify;
  }
  std::string encoded_video_params;
  base::Base64UrlEncode(serialized_video_params,
                        base::Base64UrlEncodePolicy::OMIT_PADDING,
                        &encoded_video_params);
  GURL new_url = url_to_modify;
  new_url = net::AppendOrReplaceQueryParameter(
      new_url, kVideoContextParameterKey, encoded_video_params);
  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::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);
  if (text_query.has_value() &&
      lens::features::UseVideoContextForMultimodalLensOverlayRequests()) {
    // All pages use the video context param to report page context information
    // even if the page does not contain a video.
    url_with_query_params =
        AppendVideoContextParamToURL(url_with_query_params, page_url);
  }
  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 GetTextQueryParameterValue(const GURL& url) {
  std::string param_value = "";
  net::GetValueForKeyInQuery(url, kTextQueryParameterKey, &param_value);
  return param_value;
}

const std::string GetLensModeParameterValue(const GURL& url) {
  std::string param_value = "";
  net::GetValueForKeyInQuery(url, kLensModeParameterKey, &param_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 ShouldOpenSearchURLInNewTab(const GURL& url) {
  std::string param_value;
  net::GetValueForKeyInQuery(url, kModeParameterKey, &param_value);
  const bool is_shopping_mode = param_value == kShoppingModeParameterValue;
  const bool is_mgt_mode = param_value == kMGTModeParameterValue;
  return IsValidSearchResultsUrl(url) &&
         (is_shopping_mode ||
          (is_mgt_mode && !lens::features::ShouldShowMGTInSidePanel()));
}

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,
                             &param_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