1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor_metrics.h"
6 
7 #include "base/logging.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/trace_event/trace_event.h"
10 #include "third_party/blink/renderer/core/frame/web_feature.h"
11 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
12 
13 namespace blink {
14 
15 namespace {
16 
17 const size_t kMaxTraceEventStringLength = 1000;
18 
19 }  // namespace
20 
TextFragmentAnchorMetrics(Document * document)21 TextFragmentAnchorMetrics::TextFragmentAnchorMetrics(Document* document)
22     : document_(document) {}
23 
DidCreateAnchor(int selector_count)24 void TextFragmentAnchorMetrics::DidCreateAnchor(int selector_count) {
25   UseCounter::Count(document_, WebFeature::kTextFragmentAnchor);
26   create_time_ = base::TimeTicks::Now();
27   selector_count_ = selector_count;
28 }
29 
DidFindMatch(const String text)30 void TextFragmentAnchorMetrics::DidFindMatch(const String text) {
31   matches_.push_back(text);
32 }
33 
ResetMatchCount()34 void TextFragmentAnchorMetrics::ResetMatchCount() {
35   matches_.clear();
36 }
37 
DidFindAmbiguousMatch()38 void TextFragmentAnchorMetrics::DidFindAmbiguousMatch() {
39   ambiguous_match_ = true;
40 }
41 
ScrollCancelled()42 void TextFragmentAnchorMetrics::ScrollCancelled() {
43   scroll_cancelled_ = true;
44 }
45 
DidScroll()46 void TextFragmentAnchorMetrics::DidScroll() {
47   if (first_scroll_into_view_time_.is_null())
48     first_scroll_into_view_time_ = base::TimeTicks::Now();
49 }
50 
DidNonZeroScroll()51 void TextFragmentAnchorMetrics::DidNonZeroScroll() {
52   did_non_zero_scroll_ = true;
53 }
54 
ReportMetrics()55 void TextFragmentAnchorMetrics::ReportMetrics() {
56 #ifndef NDEBUG
57   DCHECK(!metrics_reported_);
58 #endif
59   DCHECK(selector_count_);
60   DCHECK(matches_.size() <= selector_count_);
61 
62   if (matches_.size() > 0) {
63     UseCounter::Count(document_, WebFeature::kTextFragmentAnchorMatchFound);
64   }
65 
66   UMA_HISTOGRAM_COUNTS_100("TextFragmentAnchor.SelectorCount", selector_count_);
67   TRACE_EVENT_INSTANT1("blink", "TextFragmentAnchorMetrics::ReportMetrics",
68                        TRACE_EVENT_SCOPE_THREAD, "selector_count",
69                        selector_count_);
70 
71   const int match_rate_percent =
72       static_cast<int>(100 * ((matches_.size() + 0.0) / selector_count_));
73   UMA_HISTOGRAM_PERCENTAGE("TextFragmentAnchor.MatchRate", match_rate_percent);
74   TRACE_EVENT_INSTANT1("blink", "TextFragmentAnchorMetrics::ReportMetrics",
75                        TRACE_EVENT_SCOPE_THREAD, "match_rate",
76                        match_rate_percent);
77 
78   for (const String& match : matches_) {
79     TRACE_EVENT_INSTANT2("blink", "TextFragmentAnchorMetrics::ReportMetrics",
80                          TRACE_EVENT_SCOPE_THREAD, "match_found",
81                          match.Utf8().substr(0, kMaxTraceEventStringLength),
82                          "match_length", match.length());
83   }
84 
85   UMA_HISTOGRAM_BOOLEAN("TextFragmentAnchor.AmbiguousMatch", ambiguous_match_);
86   TRACE_EVENT_INSTANT1("blink", "TextFragmentAnchorMetrics::ReportMetrics",
87                        TRACE_EVENT_SCOPE_THREAD, "ambiguous_match",
88                        ambiguous_match_);
89 
90   UMA_HISTOGRAM_BOOLEAN("TextFragmentAnchor.ScrollCancelled",
91                         scroll_cancelled_);
92   TRACE_EVENT_INSTANT1("blink", "TextFragmentAnchorMetrics::ReportMetrics",
93                        TRACE_EVENT_SCOPE_THREAD, "scroll_cancelled",
94                        scroll_cancelled_);
95 
96   if (first_scroll_into_view_time_ > create_time_) {
97     UMA_HISTOGRAM_BOOLEAN("TextFragmentAnchor.DidScrollIntoView",
98                           did_non_zero_scroll_);
99     TRACE_EVENT_INSTANT1("blink", "TextFragmentAnchorMetrics::ReportMetrics",
100                          TRACE_EVENT_SCOPE_THREAD, "did_scroll_into_view",
101                          did_non_zero_scroll_);
102 
103     base::TimeDelta time_to_scroll_into_view(first_scroll_into_view_time_ -
104                                              create_time_);
105     UMA_HISTOGRAM_TIMES("TextFragmentAnchor.TimeToScrollIntoView",
106                         time_to_scroll_into_view);
107     TRACE_EVENT_INSTANT1("blink", "TextFragmentAnchorMetrics::ReportMetrics",
108                          TRACE_EVENT_SCOPE_THREAD, "time_to_scroll_into_view",
109                          time_to_scroll_into_view.InMilliseconds());
110   }
111 
112 #ifndef NDEBUG
113   metrics_reported_ = true;
114 #endif
115 }
116 
Dismissed()117 void TextFragmentAnchorMetrics::Dismissed() {
118   // We report Dismissed separately from ReportMetrics as it may or may not
119   // get called in the lifetime of the TextFragmentAnchor.
120   UseCounter::Count(document_, WebFeature::kTextFragmentAnchorTapToDismiss);
121   TRACE_EVENT_INSTANT0("blink", "TextFragmentAnchorMetrics::Dismissed",
122                        TRACE_EVENT_SCOPE_THREAD);
123 }
124 
Trace(Visitor * visitor)125 void TextFragmentAnchorMetrics::Trace(Visitor* visitor) {
126   visitor->Trace(document_);
127 }
128 
129 }  // namespace blink
130