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. 4 5 #include "chrome/browser/metrics/omnibox_metrics_provider.h" 6 7 #include <vector> 8 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" 22 23 using metrics::OmniboxEventProto; 24 25 namespace { 26 27 OmniboxEventProto::Suggestion::ResultType AsOmniboxEventResultType( 28 AutocompleteMatch::Type type) { 29 switch (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_INFINITE: 51 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST_INFINITE; 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::SEARCH_SUGGEST_ANSWER: 57 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST_ANSWER; 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: 68 break; 69 } 70 NOTREACHED(); 71 return OmniboxEventProto::Suggestion::UNKNOWN_RESULT_TYPE; 72 } 73 74 } // namespace 75 76 OmniboxMetricsProvider::OmniboxMetricsProvider() { 77 } 78 79 OmniboxMetricsProvider::~OmniboxMetricsProvider() { 80 } 81 82 void OmniboxMetricsProvider::OnRecordingEnabled() { 83 registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL, 84 content::NotificationService::AllSources()); 85 } 86 87 void OmniboxMetricsProvider::OnRecordingDisabled() { 88 registrar_.RemoveAll(); 89 } 90 91 void OmniboxMetricsProvider::ProvideGeneralMetrics( 92 metrics::ChromeUserMetricsExtension* uma_proto) { 93 uma_proto->mutable_omnibox_event()->Swap( 94 omnibox_events_cache.mutable_omnibox_event()); 95 } 96 97 void OmniboxMetricsProvider::Observe( 98 int type, 99 const content::NotificationSource& source, 100 const content::NotificationDetails& details) { 101 DCHECK_EQ(chrome::NOTIFICATION_OMNIBOX_OPENED_URL, type); 102 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()); 108 } 109 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)); 114 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); 120 } 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()); 134 } 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()); 139 } 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 146 // open. 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()); 153 } 154 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); 163 } 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); 169 } 170 } 171