[Merge-87][SH-Blink] Adding histograms for Shared Highlights.

Adding histograms to text fragment selector generation logic. Also
adding a histogram that indicates if the origin of a link with text
fragment is a known search engine.

For finding out if a given url is a known search engine,
I am separating template_url_prepopulate_data::GetEngineType and deps
into a separate file to use it from blink/renderer/platform.
Otherwise, template_url_prepopulate_data pulls deps that are also
pulled by deps which somehow triggers
AddressSanitizer: odr-violation(one definition rule) on some targets.
See https://ci.chromium.org/p/chromium/builders/try/linux-libfuzzer-asan-rel/569669

Unit tests for histograms in in a followup.

(cherry picked from commit 2fd5742a51183821b2c7fbe139b962c40e8b99c8)

Change-Id: I9d496b883f32041b5bd612b0cf8e447cc79fa68e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2432674
Commit-Queue: Gayane Petrosyan <[email protected]>
Reviewed-by: manuk hovanesian <[email protected]>
Reviewed-by: Dominic Battré <[email protected]>
Reviewed-by: Robert Kaplow <[email protected]>
Reviewed-by: David Bokan <[email protected]>
Reviewed-by: sebsg <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#813242}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2456948
Reviewed-by: Gayane Petrosyan <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#99}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/chrome/browser/prefs/pref_metrics_service.cc b/chrome/browser/prefs/pref_metrics_service.cc
index a6fb962b4..3d900e3c 100644
--- a/chrome/browser/prefs/pref_metrics_service.cc
+++ b/chrome/browser/prefs/pref_metrics_service.cc
@@ -19,7 +19,7 @@
 #include "chrome/common/url_constants.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/prefs/pref_service.h"
-#include "components/search_engines/template_url_prepopulate_data.h"
+#include "components/search_engines/search_engine_utils.h"
 #include "url/gurl.h"
 
 PrefMetricsService::PrefMetricsService(Profile* profile)
@@ -48,10 +48,9 @@
   // pages, e.g. plus.google.com).
   if (!homepage_is_ntp) {
     if (homepage_url.is_valid()) {
-      UMA_HISTOGRAM_ENUMERATION(
-          "Settings.HomePageEngineType",
-          TemplateURLPrepopulateData::GetEngineType(homepage_url),
-          SEARCH_ENGINE_MAX);
+      UMA_HISTOGRAM_ENUMERATION("Settings.HomePageEngineType",
+                                SearchEngineUtils::GetEngineType(homepage_url),
+                                SEARCH_ENGINE_MAX);
     }
   }
 }
@@ -84,10 +83,9 @@
       if (url_list->GetString(i, &url_text)) {
         GURL start_url(url_text);
         if (start_url.is_valid()) {
-          UMA_HISTOGRAM_ENUMERATION(
-              "Settings.StartupPageEngineTypes",
-              TemplateURLPrepopulateData::GetEngineType(start_url),
-              SEARCH_ENGINE_MAX);
+          UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageEngineTypes",
+                                    SearchEngineUtils::GetEngineType(start_url),
+                                    SEARCH_ENGINE_MAX);
         }
       }
     }
@@ -100,10 +98,9 @@
   for (size_t i = 0; i < startup_tabs.size(); ++i) {
     GURL start_url(startup_tabs.at(i).url);
     if (start_url.is_valid()) {
-      UMA_HISTOGRAM_ENUMERATION(
-          "Settings.PinnedTabEngineTypes",
-          TemplateURLPrepopulateData::GetEngineType(start_url),
-          SEARCH_ENGINE_MAX);
+      UMA_HISTOGRAM_ENUMERATION("Settings.PinnedTabEngineTypes",
+                                SearchEngineUtils::GetEngineType(start_url),
+                                SEARCH_ENGINE_MAX);
     }
   }
 #endif
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index b7a2bf5..58132b9e 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -28,8 +28,8 @@
 #include "components/omnibox/browser/omnibox_pedal.h"
 #include "components/omnibox/browser/suggestion_answer.h"
 #include "components/omnibox/common/omnibox_features.h"
+#include "components/search_engines/search_engine_utils.h"
 #include "components/search_engines/template_url.h"
-#include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "ui/gfx/vector_icon_types.h"
@@ -842,7 +842,7 @@
   if (template_url) {
     SearchEngineType search_engine_type =
         match.destination_url.is_valid()
-            ? TemplateURLPrepopulateData::GetEngineType(match.destination_url)
+            ? SearchEngineUtils::GetEngineType(match.destination_url)
             : SEARCH_ENGINE_OTHER;
     UMA_HISTOGRAM_ENUMERATION("Omnibox.SearchEngineType", search_engine_type,
                               SEARCH_ENGINE_MAX);
diff --git a/components/search_engines/BUILD.gn b/components/search_engines/BUILD.gn
index 1f7e2e69..eb66a2d9 100644
--- a/components/search_engines/BUILD.gn
+++ b/components/search_engines/BUILD.gn
@@ -52,6 +52,7 @@
   public_deps = [
     ":prepopulated_engines",
     ":search_engine_type",
+    ":search_engine_utils",
     "//base",
     "//components/google/core/common",
     "//components/keyed_service/core",
@@ -100,6 +101,28 @@
   }
 }
 
+source_set("search_engine_utils") {
+  sources = [
+    "search_engine_utils.cc",
+    "search_engine_utils.h",
+  ]
+
+  public_deps = [
+    ":prepopulated_engines",
+    ":search_engine_type",
+    "//components/google/core/common",
+  ]
+
+  deps = [ "//url" ]
+
+  if (is_android) {
+    deps += [
+      "//components/search_engines/android:jni_headers",
+      "//url:gurl_android",
+    ]
+  }
+}
+
 source_set("search_engine_type") {
   sources = [ "search_engine_type.h" ]
 }
diff --git a/components/search_engines/search_engine_utils.cc b/components/search_engines/search_engine_utils.cc
new file mode 100644
index 0000000..ecbd1561
--- /dev/null
+++ b/components/search_engines/search_engine_utils.cc
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/search_engines/search_engine_utils.h"
+
+#include "components/google/core/common/google_util.h"
+#include "components/search_engines/prepopulated_engines.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "url/gurl.h"
+
+namespace SearchEngineUtils {
+
+namespace {
+
+bool SameDomain(const GURL& given_url, const GURL& prepopulated_url) {
+  return prepopulated_url.is_valid() &&
+         net::registry_controlled_domains::SameDomainOrHost(
+             given_url, prepopulated_url,
+             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+}
+
+}  // namespace
+
+// Global functions -----------------------------------------------------------
+
+SearchEngineType GetEngineType(const GURL& url) {
+  DCHECK(url.is_valid());
+
+  // Check using TLD+1s, in order to more aggressively match search engine types
+  // for data imported from other browsers.
+  //
+  // First special-case Google, because the prepopulate URL for it will not
+  // convert to a GURL and thus won't have an origin.  Instead see if the
+  // incoming URL's host is "[*.]google.<TLD>".
+  if (google_util::IsGoogleHostname(url.host(),
+                                    google_util::DISALLOW_SUBDOMAIN))
+    return TemplateURLPrepopulateData::google.type;
+
+  // Now check the rest of the prepopulate data.
+  for (size_t i = 0; i < TemplateURLPrepopulateData::kAllEnginesLength; ++i) {
+    // First check the main search URL.
+    if (SameDomain(
+            url, GURL(TemplateURLPrepopulateData::kAllEngines[i]->search_url)))
+      return TemplateURLPrepopulateData::kAllEngines[i]->type;
+
+    // Then check the alternate URLs.
+    for (size_t j = 0;
+         j < TemplateURLPrepopulateData::kAllEngines[i]->alternate_urls_size;
+         ++j) {
+      if (SameDomain(url, GURL(TemplateURLPrepopulateData::kAllEngines[i]
+                                   ->alternate_urls[j])))
+        return TemplateURLPrepopulateData::kAllEngines[i]->type;
+    }
+  }
+
+  return SEARCH_ENGINE_OTHER;
+}
+
+}  // namespace SearchEngineUtils
diff --git a/components/search_engines/search_engine_utils.h b/components/search_engines/search_engine_utils.h
new file mode 100644
index 0000000..83e0dd4
--- /dev/null
+++ b/components/search_engines/search_engine_utils.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEARCH_ENGINES_SEARCH_ENGINE_UTILS_H_
+#define COMPONENTS_SEARCH_ENGINES_SEARCH_ENGINE_UTILS_H_
+
+#include "components/search_engines/search_engine_type.h"
+
+class GURL;
+
+namespace SearchEngineUtils {
+
+// Like the above, but takes a GURL which is expected to represent a search URL.
+// This may be called on any thread.
+SearchEngineType GetEngineType(const GURL& url);
+
+}  // namespace SearchEngineUtils
+
+#endif  // COMPONENTS_SEARCH_ENGINES_SEARCH_ENGINE_UTILS_H_
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index e881a98..2a4c441 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -26,9 +26,9 @@
 #include "base/trace_event/memory_usage_estimator.h"
 #include "build/build_config.h"
 #include "components/google/core/common/google_util.h"
+#include "components/search_engines/search_engine_utils.h"
 #include "components/search_engines/search_engines_switches.h"
 #include "components/search_engines/search_terms_data.h"
-#include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/url_formatter/url_formatter.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/escape.h"
@@ -1410,8 +1410,8 @@
     const SearchTermsData& search_terms_data) const {
   if (engine_type_ == SEARCH_ENGINE_UNKNOWN) {
     const GURL url = GenerateSearchURL(search_terms_data);
-    engine_type_ = url.is_valid() ?
-        TemplateURLPrepopulateData::GetEngineType(url) : SEARCH_ENGINE_OTHER;
+    engine_type_ = url.is_valid() ? SearchEngineUtils::GetEngineType(url)
+                                  : SEARCH_ENGINE_OTHER;
     DCHECK_NE(SEARCH_ENGINE_UNKNOWN, engine_type_);
   }
   return engine_type_;
diff --git a/components/search_engines/template_url_prepopulate_data.cc b/components/search_engines/template_url_prepopulate_data.cc
index 60c60a4d..c1368c6 100644
--- a/components/search_engines/template_url_prepopulate_data.cc
+++ b/components/search_engines/template_url_prepopulate_data.cc
@@ -8,15 +8,12 @@
 #include "base/stl_util.h"
 #include "build/build_config.h"
 #include "components/country_codes/country_codes.h"
-#include "components/google/core/common/google_util.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/prepopulated_engines.h"
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_data_util.h"
-#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "url/gurl.h"
 
 namespace TemplateURLPrepopulateData {
 
@@ -1356,13 +1353,6 @@
   return t_urls;
 }
 
-bool SameDomain(const GURL& given_url, const GURL& prepopulated_url) {
-  return prepopulated_url.is_valid() &&
-      net::registry_controlled_domains::SameDomainOrHost(
-          given_url, prepopulated_url,
-          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-}
-
 }  // namespace
 
 // Global functions -----------------------------------------------------------
@@ -1452,33 +1442,4 @@
              : nullptr;
 }
 
-SearchEngineType GetEngineType(const GURL& url) {
-  DCHECK(url.is_valid());
-
-  // Check using TLD+1s, in order to more aggressively match search engine types
-  // for data imported from other browsers.
-  //
-  // First special-case Google, because the prepopulate URL for it will not
-  // convert to a GURL and thus won't have an origin.  Instead see if the
-  // incoming URL's host is "[*.]google.<TLD>".
-  if (google_util::IsGoogleHostname(url.host(),
-                                    google_util::DISALLOW_SUBDOMAIN))
-    return google.type;
-
-  // Now check the rest of the prepopulate data.
-  for (size_t i = 0; i < kAllEnginesLength; ++i) {
-    // First check the main search URL.
-    if (SameDomain(url, GURL(kAllEngines[i]->search_url)))
-      return kAllEngines[i]->type;
-
-    // Then check the alternate URLs.
-    for (size_t j = 0; j < kAllEngines[i]->alternate_urls_size; ++j) {
-      if (SameDomain(url, GURL(kAllEngines[i]->alternate_urls[j])))
-        return kAllEngines[i]->type;
-    }
-  }
-
-  return SEARCH_ENGINE_OTHER;
-}
-
 }  // namespace TemplateURLPrepopulateData
diff --git a/components/search_engines/template_url_prepopulate_data.h b/components/search_engines/template_url_prepopulate_data.h
index 159263ac..682060b 100644
--- a/components/search_engines/template_url_prepopulate_data.h
+++ b/components/search_engines/template_url_prepopulate_data.h
@@ -11,9 +11,6 @@
 #include <string>
 #include <vector>
 
-#include "components/search_engines/search_engine_type.h"
-
-class GURL;
 class PrefService;
 struct TemplateURLData;
 
@@ -65,10 +62,6 @@
 std::unique_ptr<TemplateURLData> GetPrepopulatedDefaultSearch(
     PrefService* prefs);
 
-// Like the above, but takes a GURL which is expected to represent a search URL.
-// This may be called on any thread.
-SearchEngineType GetEngineType(const GURL& url);
-
 }  // namespace TemplateURLPrepopulateData
 
 #endif  // COMPONENTS_SEARCH_ENGINES_TEMPLATE_URL_PREPOPULATE_DATA_H_
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
index f386226e..ad820ba 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector.h"
 #include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
+#include "third_party/blink/renderer/platform/search_engine_utils.h"
 
 namespace blink {
 
@@ -377,6 +378,7 @@
   did_find_match_ = true;
 
   if (first_match_needs_scroll_) {
+    metrics_->SetSearchEngineSource(HasSearchEngineSource());
     first_match_needs_scroll_ = false;
 
     PhysicalRect bounding_box(ComputeTextRect(range));
@@ -498,4 +500,13 @@
   metrics_->SetTickClockForTesting(tick_clock);
 }
 
+bool TextFragmentAnchor::HasSearchEngineSource() {
+  AtomicString referrer = frame_->GetDocument()->referrer();
+  // TODO(crbug.com/1133823): Add test case for valid referrer.
+  if (!referrer)
+    return false;
+
+  return IsKnownSearchEngine(referrer);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
index 22cf488..ee4d075 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h
@@ -95,6 +95,8 @@
 
   void FireBeforeMatchEvent(Element* element);
 
+  bool HasSearchEngineSource();
+
   Vector<TextFragmentFinder> text_fragment_finders_;
 
   Member<LocalFrame> frame_;
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.cc
index 10fdb64..e100957 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.cc
@@ -150,7 +150,7 @@
     }
 
     UMA_HISTOGRAM_ENUMERATION("TextFragmentAnchor.Parameters",
-                              GetParametersForMatch(match));
+                              GetParametersForSelector(match.selector));
   }
 
   UMA_HISTOGRAM_BOOLEAN("TextFragmentAnchor.AmbiguousMatch", ambiguous_match_);
@@ -182,6 +182,8 @@
                          time_to_scroll_into_view.InMilliseconds());
   }
 
+  UMA_HISTOGRAM_BOOLEAN("TextFragmentAnchor.LinkOpenSource",
+                        has_search_engine_source_);
 #ifndef NDEBUG
   metrics_reported_ = true;
 #endif
@@ -200,26 +202,26 @@
 }
 
 TextFragmentAnchorMetrics::TextFragmentAnchorParameters
-TextFragmentAnchorMetrics::GetParametersForMatch(const Match& match) {
+TextFragmentAnchorMetrics::GetParametersForSelector(
+    const TextFragmentSelector& selector) {
   TextFragmentAnchorParameters parameters =
       TextFragmentAnchorParameters::kUnknown;
 
-  if (match.selector.Type() == TextFragmentSelector::SelectorType::kExact) {
-    if (match.selector.Prefix().length() && match.selector.Suffix().length())
+  if (selector.Type() == TextFragmentSelector::SelectorType::kExact) {
+    if (selector.Prefix().length() && selector.Suffix().length())
       parameters = TextFragmentAnchorParameters::kExactTextWithContext;
-    else if (match.selector.Prefix().length())
+    else if (selector.Prefix().length())
       parameters = TextFragmentAnchorParameters::kExactTextWithPrefix;
-    else if (match.selector.Suffix().length())
+    else if (selector.Suffix().length())
       parameters = TextFragmentAnchorParameters::kExactTextWithSuffix;
     else
       parameters = TextFragmentAnchorParameters::kExactText;
-  } else if (match.selector.Type() ==
-             TextFragmentSelector::SelectorType::kRange) {
-    if (match.selector.Prefix().length() && match.selector.Suffix().length())
+  } else if (selector.Type() == TextFragmentSelector::SelectorType::kRange) {
+    if (selector.Prefix().length() && selector.Suffix().length())
       parameters = TextFragmentAnchorParameters::kTextRangeWithContext;
-    else if (match.selector.Prefix().length())
+    else if (selector.Prefix().length())
       parameters = TextFragmentAnchorParameters::kTextRangeWithPrefix;
-    else if (match.selector.Suffix().length())
+    else if (selector.Suffix().length())
       parameters = TextFragmentAnchorParameters::kTextRangeWithSuffix;
     else
       parameters = TextFragmentAnchorParameters::kTextRange;
@@ -233,4 +235,9 @@
   tick_clock_ = tick_clock;
 }
 
+void TextFragmentAnchorMetrics::SetSearchEngineSource(
+    bool has_search_engine_source) {
+  has_search_engine_source_ = has_search_engine_source;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.h
index c7b177d1..616beb85 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.h
@@ -42,6 +42,9 @@
 
   explicit TextFragmentAnchorMetrics(Document* document);
 
+  static TextFragmentAnchorParameters GetParametersForSelector(
+      const TextFragmentSelector& selector);
+
   void DidCreateAnchor(int selector_count, int directive_length);
 
   void DidFindMatch(Match match);
@@ -65,11 +68,11 @@
 
   void SetTickClockForTesting(const base::TickClock* tick_clock);
 
+  void SetSearchEngineSource(bool has_search_engine_source);
+
   void Trace(Visitor*) const;
 
  private:
-  TextFragmentAnchorParameters GetParametersForMatch(const Match& match);
-
   Member<Document> document_;
 
 #ifndef NDEBUG
@@ -85,6 +88,7 @@
   base::TimeTicks first_scroll_into_view_time_;
   bool did_non_zero_scroll_ = false;
   bool did_scroll_to_top_ = false;
+  bool has_search_engine_source_ = false;
 
   const base::TickClock* tick_clock_;
 };
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc
index 5c57efd6..dcde1c1 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics_test.cc
@@ -137,6 +137,9 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 1);
   histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.TableCellMatch", 0,
                                        1);
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 1);
+  histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.LinkOpenSource", 0,
+                                       1);
 }
 
 // Test UMA metrics collection when there is no match found
@@ -199,6 +202,10 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.ListItemMatch", 0);
 
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 0);
+
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 1);
+  histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.LinkOpenSource", 0,
+                                       1);
 }
 
 // Test that we don't collect any metrics when there is no text directive
@@ -245,6 +252,8 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.ListItemMatch", 0);
 
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 0);
+
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 0);
 }
 
 // Test that the correct metrics are collected when we found a match but didn't
@@ -312,6 +321,10 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 1);
   histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.TableCellMatch", 0,
                                        1);
+
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 1);
+  histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.LinkOpenSource", 0,
+                                       1);
 }
 
 // Test that the correct metrics are collected for all possible combinations of
@@ -405,6 +418,10 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 4);
   histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.TableCellMatch", 0,
                                        4);
+
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 1);
+  histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.LinkOpenSource", 0,
+                                       1);
 }
 
 // Test that the correct metrics are collected for all possible combinations of
@@ -510,6 +527,10 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.ListItemMatch", 0);
 
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 0);
+
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 1);
+  histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.LinkOpenSource", 0,
+                                       1);
 }
 
 class TextFragmentAnchorScrollMetricsTest
@@ -635,6 +656,10 @@
   histogram_tester_.ExpectTotalCount("TextFragmentAnchor.TableCellMatch", 1);
   histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.TableCellMatch", 0,
                                        1);
+
+  histogram_tester_.ExpectTotalCount("TextFragmentAnchor.LinkOpenSource", 1);
+  histogram_tester_.ExpectUniqueSample("TextFragmentAnchor.LinkOpenSource", 0,
+                                       1);
 }
 
 // Test that the user scrolling back to the top of the page reports metrics
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
index 19cf8c3..282a10b 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.cc
@@ -4,12 +4,15 @@
 
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h"
 
+#include "base/metrics/histogram_macros.h"
+#include "base/time/default_tick_clock.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/platform/interface_registry.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/finder/find_buffer.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.h"
 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h"
 #include "third_party/blink/renderer/platform/text/text_boundaries.h"
 
@@ -157,6 +160,7 @@
 constexpr int kNoContextMinChars = 20;
 constexpr int kMaxContextWords = 10;
 constexpr int kMaxRangeWords = 10;
+constexpr int kMaxIterationCountToRecord = 10;
 
 void TextFragmentSelectorGenerator::UpdateSelection(
     LocalFrame* selection_frame,
@@ -228,6 +232,7 @@
   DCHECK(selection_range_);
   DCHECK(callback);
 
+  generation_start_time_ = base::DefaultTickClock::GetInstance()->NowTicks();
   pending_generate_selector_callback_ = std::move(callback);
   state_ = kNeedsNewCandidate;
   step_ = kExact;
@@ -235,8 +240,13 @@
   max_available_suffix_ = "";
   num_prefix_words_ = 0;
   num_suffix_words_ = 0;
+  iteration_ = 0;
 
   CompleteSelection();
+  UMA_HISTOGRAM_COUNTS_1000(
+      "SharedHighlights.LinkGenerated.SelectionLength",
+      PlainText(EphemeralRange(selection_range_)).length());
+
   GenerateSelectorCandidate();
 }
 
@@ -274,7 +284,7 @@
 
 void TextFragmentSelectorGenerator::RunTextFinder() {
   DCHECK(selector_);
-
+  iteration_++;
   // |FindMatch| will call |DidFindMatch| indicating if the match was unique.
   TextFragmentFinder finder(*this, *selector_);
   finder.FindMatch(*selection_frame_->GetDocument());
@@ -301,6 +311,8 @@
 }
 
 void TextFragmentSelectorGenerator::NoMatchFound() {
+  UMA_HISTOGRAM_ENUMERATION("SharedHighlights.LinkGenerated.Error",
+                            LinkGenerationError::kIncorrectSelector);
   NotifySelectorReady(
       TextFragmentSelector(TextFragmentSelector::SelectorType::kInvalid));
 }
@@ -308,6 +320,33 @@
 void TextFragmentSelectorGenerator::NotifySelectorReady(
     const TextFragmentSelector& selector) {
   DCHECK(pending_generate_selector_callback_);
+  // TODO(crbug.com/1133823): Add unit tests for all SharedHighlights.*
+  // histograms.
+  UMA_HISTOGRAM_BOOLEAN(
+      "SharedHighlights.LinkGenerated",
+      selector.Type() != TextFragmentSelector::SelectorType::kInvalid);
+
+  if (selector.Type() != TextFragmentSelector::SelectorType::kInvalid) {
+    UMA_HISTOGRAM_COUNTS_1000("SharedHighlights.LinkGenerated.ParamLength",
+                              selector.ToString().length());
+
+    UMA_HISTOGRAM_EXACT_LINEAR("SharedHighlights.LinkGenerated.Iterations",
+                               iteration_, kMaxIterationCountToRecord);
+    UMA_HISTOGRAM_TIMES("SharedHighlights.LinkGenerated.TimeToGenerate",
+                        base::DefaultTickClock::GetInstance()->NowTicks() -
+                            generation_start_time_);
+    UMA_HISTOGRAM_ENUMERATION(
+        "SharedHighlights.LinkGenerated.SelectorParameters",
+        TextFragmentAnchorMetrics::GetParametersForSelector(selector));
+  } else {
+    UMA_HISTOGRAM_EXACT_LINEAR(
+        "SharedHighlights.LinkGenerated.Error.Iterations", iteration_,
+        kMaxIterationCountToRecord);
+    UMA_HISTOGRAM_TIMES("SharedHighlights.LinkGenerated.Error.TimeToGenerate",
+                        base::DefaultTickClock::GetInstance()->NowTicks() -
+                            generation_start_time_);
+  }
+
   std::move(pending_generate_selector_callback_).Run(selector.ToString());
 }
 
@@ -351,6 +390,8 @@
 
   String selected_text = PlainText(ephemeral_range).StripWhiteSpace();
   if (selected_text.IsEmpty()) {
+    UMA_HISTOGRAM_ENUMERATION("SharedHighlights.LinkGenerated.Error",
+                              LinkGenerationError::kEmptySelection);
     state_ = kFailure;
     return;
   }
@@ -411,6 +452,8 @@
       // If from middle till end of selection there is no word break, then we
       // cannot use it for range end.
       if (mid_point == selection_length) {
+        UMA_HISTOGRAM_ENUMERATION("SharedHighlights.LinkGenerated.Error",
+                                  LinkGenerationError::kNoRange);
         state_ = kFailure;
         return;
       }
@@ -451,6 +494,8 @@
   // Give up if context is already too long.
   if (num_prefix_words_ == kMaxContextWords ||
       num_prefix_words_ == kMaxContextWords) {
+    UMA_HISTOGRAM_ENUMERATION("SharedHighlights.LinkGenerated.Error",
+                              LinkGenerationError::kContextLimitReached);
     state_ = kFailure;
     return;
   }
@@ -463,6 +508,8 @@
   }
 
   if (max_available_prefix_.IsEmpty() && max_available_suffix_.IsEmpty()) {
+    UMA_HISTOGRAM_ENUMERATION("SharedHighlights.LinkGenerated.Error",
+                              LinkGenerationError::kNoContext);
     state_ = kFailure;
     return;
   }
@@ -472,6 +519,8 @@
 
   // Give up if we were unable to get new prefix and suffix.
   if (prefix == selector_->Prefix() && suffix == selector_->Suffix()) {
+    UMA_HISTOGRAM_ENUMERATION("SharedHighlights.LinkGenerated.Error",
+                              LinkGenerationError::kContextExhausted);
     state_ = kFailure;
     return;
   }
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
index 5f7c047..14272be 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_selector_generator.h
@@ -31,6 +31,17 @@
       public TextFragmentFinder::Client,
       public blink::mojom::blink::TextFragmentSelectorProducer {
  public:
+  // Update corresponding |LinkGenerationError| in enums.xml.
+  enum LinkGenerationError {
+    kIncorrectSelector,
+    kNoRange,
+    kNoContext,
+    kContextExhausted,
+    kContextLimitReached,
+    kEmptySelection,
+
+    kMaxValue = kContextLimitReached
+  };
   explicit TextFragmentSelectorGenerator() = default;
 
   void BindTextFragmentSelectorProducer(
@@ -140,6 +151,9 @@
   int num_range_start_words_ = 0;
   int num_range_end_words_ = 0;
 
+  int iteration_ = 0;
+  base::TimeTicks generation_start_time_;
+
   DISALLOW_COPY_AND_ASSIGN(TextFragmentSelectorGenerator);
 };
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 6e76f3d..145eb03 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1361,6 +1361,8 @@
     "privacy_budget/identifiability_digest_helpers.cc",
     "privacy_budget/identifiability_digest_helpers.h",
     "resolution_units.h",
+    "search_engine_utils.cc",
+    "search_engine_utils.h",
     "supplementable.cc",
     "supplementable.h",
     "text/bidi_character_run.h",
@@ -1607,6 +1609,7 @@
     "//base/allocator:buildflags",
     "//cc/ipc",
     "//components/paint_preview/common",
+    "//components/search_engines:search_engine_utils",
     "//components/viz/client",
     "//components/viz/common",
     "//crypto",
diff --git a/third_party/blink/renderer/platform/DEPS b/third_party/blink/renderer/platform/DEPS
index 8b2054f6..b6e1dc15 100644
--- a/third_party/blink/renderer/platform/DEPS
+++ b/third_party/blink/renderer/platform/DEPS
@@ -97,4 +97,7 @@
     "ukm_time_aggregator_test.cc" : [
         "+components/ukm/test_ukm_recorder.h"
     ],
+    "search_engine_utils.cc": [
+        "+components/search_engines/search_engine_utils.h"
+    ],
 }
diff --git a/third_party/blink/renderer/platform/search_engine_utils.cc b/third_party/blink/renderer/platform/search_engine_utils.cc
new file mode 100644
index 0000000..02e013a9
--- /dev/null
+++ b/third_party/blink/renderer/platform/search_engine_utils.cc
@@ -0,0 +1,18 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/search_engine_utils.h"
+
+#include "components/search_engines/search_engine_utils.h"
+#include "url/gurl.h"
+
+namespace blink {
+
+bool IsKnownSearchEngine(const AtomicString& url) {
+  GURL gurl(url.GetString().Utf8());
+
+  return SearchEngineUtils::GetEngineType(gurl) > 0;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/search_engine_utils.h b/third_party/blink/renderer/platform/search_engine_utils.h
new file mode 100644
index 0000000..551ead90
--- /dev/null
+++ b/third_party/blink/renderer/platform/search_engine_utils.h
@@ -0,0 +1,18 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SEARCH_ENGINE_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SEARCH_ENGINE_UTILS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+// Returns whether provided string is a URL of a known search engine.
+PLATFORM_EXPORT bool IsKnownSearchEngine(const AtomicString&);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SEARCH_ENGINE_UTILS_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 3cba4c2..7ac44bcc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -40194,6 +40194,15 @@
   <int value="20" label="kOtherReason"/>
 </enum>
 
+<enum name="LinkGenerationError">
+  <int value="0" label="Incorrect selector"/>
+  <int value="1" label="No range available"/>
+  <int value="2" label="No context available"/>
+  <int value="3" label="Available context exhausted"/>
+  <int value="4" label="Context word limit reached"/>
+  <int value="5" label="Empty selection"/>
+</enum>
+
 <enum name="LinkMonitorFailureType">
   <int value="0" label="Local MAC Address Not Found"/>
   <int value="1" label="Client Startup Failure"/>
@@ -70736,6 +70745,11 @@
   <int value="8" label="TextRangeWithContext"/>
 </enum>
 
+<enum name="TextFragmentLinkOpenSource">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="SearchEngine"/>
+</enum>
+
 <enum name="TextToSpeechEvent">
   <int value="0" label="Start"/>
   <int value="1" label="End"/>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 9123364..df62208 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -12659,6 +12659,96 @@
   <summary>Win32 APIs that error out during setup.</summary>
 </histogram>
 
+<histogram name="SharedHighlights.LinkGenerated" units="BooleanSuccess"
+    expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Indicates whether text fragment selector generation was successful.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.Error"
+    enum="LinkGenerationError" expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Indicates error that caused text fragment selector generation to fail.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.Error.Iterations"
+    units="iterations" expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Indicates number of iterations it took for the unsuccessful text fragment
+    selector generation run.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.Error.TimeToGenerate"
+    units="ms" expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Time it took in millisecons for a failed text fragment selector generation
+    attempt.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.Iterations" units="iterations"
+    expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Indicates number of iterations it took to successfully generate text
+    fragment selector.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.ParamLength" units="characters"
+    expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Number of charaters in successfully generated text fragment selector. For
+    example, for #:=:text=sample%20text, it would be the
+    len(&quot;sample%20text&quot;) which is 13. Recorded only when selector
+    generation was successful.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.SelectionLength"
+    units="characters" expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Number of characters in the user selected text, that text fragment selector
+    is generated for. Recorded regardless of generation success.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.SelectorParameters"
+    enum="TextFragmentAnchorParameters" expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Indicates which parameters were specified in a text fragment selector.
+    Recorded only when selector generation was successful.
+  </summary>
+</histogram>
+
+<histogram name="SharedHighlights.LinkGenerated.TimeToGenerate" units="ms"
+    expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    How long it took in milliseconds to successfully generate text fragment
+    selector.
+  </summary>
+</histogram>
+
 <histogram name="SharedMemory.MapBlockedForSecurity" enum="BooleanBlocked"
     expires_after="M85">
   <owner>[email protected]</owner>
@@ -14232,6 +14322,17 @@
   </summary>
 </histogram>
 
+<histogram name="TextFragmentAnchor.LinkOpenSource"
+    enum="TextFragmentLinkOpenSource" expires_after="2021-03-07">
+  <owner>[email protected]</owner>
+  <owner>[email protected]</owner>
+  <summary>
+    Recorded for every navigatin to a link with a valid text fragment selector
+    (e.g. #:=:text=SELECTOR). Indicates source type that navigation originated
+    from.
+  </summary>
+</histogram>
+
 <histogram name="TextFragmentAnchor.ListItemMatch" units="Boolean"
     expires_after="2021-03-07">
   <owner>[email protected]</owner>