1 // Copyright 2014 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.
5 #include "chrome/browser/metrics/omnibox_metrics_provider.h"
9 #include "base/logging.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/omnibox/omnibox_log.h"
14 #include "chrome/browser/ui/browser_otr_state.h"
15 #include "components/metrics/metrics_log.h"
16 #include "components/metrics/proto/omnibox_event.pb.h"
17 #include "components/metrics/proto/omnibox_input_type.pb.h"
18 #include "components/omnibox/autocomplete_match.h"
19 #include "components/omnibox/autocomplete_provider.h"
20 #include "components/omnibox/autocomplete_result.h"
21 #include "content/public/browser/notification_service.h"
23 using metrics::OmniboxEventProto
;
27 OmniboxEventProto::Suggestion::ResultType
AsOmniboxEventResultType(
28 AutocompleteMatch::Type type
) {
30 case AutocompleteMatchType::URL_WHAT_YOU_TYPED
:
31 return OmniboxEventProto::Suggestion::URL_WHAT_YOU_TYPED
;
32 case AutocompleteMatchType::HISTORY_URL
:
33 return OmniboxEventProto::Suggestion::HISTORY_URL
;
34 case AutocompleteMatchType::HISTORY_TITLE
:
35 return OmniboxEventProto::Suggestion::HISTORY_TITLE
;
36 case AutocompleteMatchType::HISTORY_BODY
:
37 return OmniboxEventProto::Suggestion::HISTORY_BODY
;
38 case AutocompleteMatchType::HISTORY_KEYWORD
:
39 return OmniboxEventProto::Suggestion::HISTORY_KEYWORD
;
40 case AutocompleteMatchType::NAVSUGGEST
:
41 return OmniboxEventProto::Suggestion::NAVSUGGEST
;
42 case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED
:
43 return OmniboxEventProto::Suggestion::SEARCH_WHAT_YOU_TYPED
;
44 case AutocompleteMatchType::SEARCH_HISTORY
:
45 return OmniboxEventProto::Suggestion::SEARCH_HISTORY
;
46 case AutocompleteMatchType::SEARCH_SUGGEST
:
47 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST
;
48 case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY
:
49 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST_ENTITY
;
50 case AutocompleteMatchType::SEARCH_SUGGEST_TAIL
:
51 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST_TAIL
;
52 case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED
:
53 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST_PERSONALIZED
;
54 case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE
:
55 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST_PROFILE
;
56 case AutocompleteMatchType::CALCULATOR
:
57 return OmniboxEventProto::Suggestion::CALCULATOR
;
58 case AutocompleteMatchType::SEARCH_OTHER_ENGINE
:
59 return OmniboxEventProto::Suggestion::SEARCH_OTHER_ENGINE
;
60 case AutocompleteMatchType::EXTENSION_APP
:
61 return OmniboxEventProto::Suggestion::EXTENSION_APP
;
62 case AutocompleteMatchType::BOOKMARK_TITLE
:
63 return OmniboxEventProto::Suggestion::BOOKMARK_TITLE
;
64 case AutocompleteMatchType::NAVSUGGEST_PERSONALIZED
:
65 return OmniboxEventProto::Suggestion::NAVSUGGEST_PERSONALIZED
;
66 case AutocompleteMatchType::CONTACT_DEPRECATED
:
67 case AutocompleteMatchType::NUM_TYPES
:
71 return OmniboxEventProto::Suggestion::UNKNOWN_RESULT_TYPE
;
76 OmniboxMetricsProvider::OmniboxMetricsProvider() {
79 OmniboxMetricsProvider::~OmniboxMetricsProvider() {
82 void OmniboxMetricsProvider::OnRecordingEnabled() {
83 registrar_
.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL
,
84 content::NotificationService::AllSources());
87 void OmniboxMetricsProvider::OnRecordingDisabled() {
88 registrar_
.RemoveAll();
91 void OmniboxMetricsProvider::ProvideGeneralMetrics(
92 metrics::ChromeUserMetricsExtension
* uma_proto
) {
93 uma_proto
->mutable_omnibox_event()->Swap(
94 omnibox_events_cache
.mutable_omnibox_event());
97 void OmniboxMetricsProvider::Observe(
99 const content::NotificationSource
& source
,
100 const content::NotificationDetails
& details
) {
101 DCHECK_EQ(chrome::NOTIFICATION_OMNIBOX_OPENED_URL
, type
);
103 // We simply don't log events to UMA if there is a single incognito
104 // session visible. In the future, it may be worth revisiting this to
105 // still log events from non-incognito sessions.
106 if (!chrome::IsOffTheRecordSessionActive())
107 RecordOmniboxOpenedURL(*content::Details
<OmniboxLog
>(details
).ptr());
110 void OmniboxMetricsProvider::RecordOmniboxOpenedURL(const OmniboxLog
& log
) {
111 std::vector
<base::string16
> terms
;
112 const int num_terms
=
113 static_cast<int>(Tokenize(log
.text
, base::kWhitespaceUTF16
, &terms
));
115 OmniboxEventProto
* omnibox_event
= omnibox_events_cache
.add_omnibox_event();
116 omnibox_event
->set_time(metrics::MetricsLog::GetCurrentTime());
117 if (log
.tab_id
!= -1) {
118 // If we know what tab the autocomplete URL was opened in, log it.
119 omnibox_event
->set_tab_id(log
.tab_id
);
121 omnibox_event
->set_typed_length(log
.text
.length());
122 omnibox_event
->set_just_deleted_text(log
.just_deleted_text
);
123 omnibox_event
->set_num_typed_terms(num_terms
);
124 omnibox_event
->set_selected_index(log
.selected_index
);
125 if (log
.completed_length
!= base::string16::npos
)
126 omnibox_event
->set_completed_length(log
.completed_length
);
127 const base::TimeDelta default_time_delta
=
128 base::TimeDelta::FromMilliseconds(-1);
129 if (log
.elapsed_time_since_user_first_modified_omnibox
!=
130 default_time_delta
) {
131 // Only upload the typing duration if it is set/valid.
132 omnibox_event
->set_typing_duration_ms(
133 log
.elapsed_time_since_user_first_modified_omnibox
.InMilliseconds());
135 if (log
.elapsed_time_since_last_change_to_default_match
!=
136 default_time_delta
) {
137 omnibox_event
->set_duration_since_last_default_match_update_ms(
138 log
.elapsed_time_since_last_change_to_default_match
.InMilliseconds());
140 omnibox_event
->set_current_page_classification(
141 log
.current_page_classification
);
142 omnibox_event
->set_input_type(log
.input_type
);
143 // We consider a paste-and-search/paste-and-go action to have a closed popup
144 // (as explained in omnibox_event.proto) even if it was not, because such
145 // actions ignore the contents of the popup so it doesn't matter that it was
147 const bool consider_popup_open
= log
.is_popup_open
&& !log
.is_paste_and_go
;
148 omnibox_event
->set_is_popup_open(consider_popup_open
);
149 omnibox_event
->set_is_paste_and_go(log
.is_paste_and_go
);
150 if (consider_popup_open
) {
151 omnibox_event
->set_is_top_result_hidden_in_dropdown(
152 log
.result
.ShouldHideTopMatch());
155 for (AutocompleteResult::const_iterator
i(log
.result
.begin());
156 i
!= log
.result
.end(); ++i
) {
157 OmniboxEventProto::Suggestion
* suggestion
= omnibox_event
->add_suggestion();
158 suggestion
->set_provider(i
->provider
->AsOmniboxEventProviderType());
159 suggestion
->set_result_type(AsOmniboxEventResultType(i
->type
));
160 suggestion
->set_relevance(i
->relevance
);
161 if (i
->typed_count
!= -1)
162 suggestion
->set_typed_count(i
->typed_count
);
164 for (ProvidersInfo::const_iterator
i(log
.providers_info
.begin());
165 i
!= log
.providers_info
.end(); ++i
) {
166 OmniboxEventProto::ProviderInfo
* provider_info
=
167 omnibox_event
->add_provider_info();
168 provider_info
->CopyFrom(*i
);