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 23using metrics::OmniboxEventProto; 24 25namespace { 26 27OmniboxEventProto::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 76OmniboxMetricsProvider::OmniboxMetricsProvider() { 77} 78 79OmniboxMetricsProvider::~OmniboxMetricsProvider() { 80} 81 82void OmniboxMetricsProvider::OnRecordingEnabled() { 83 registrar_.Add(this, chrome::NOTIFICATION_OMNIBOX_OPENED_URL, 84 content::NotificationService::AllSources()); 85} 86 87void OmniboxMetricsProvider::OnRecordingDisabled() { 88 registrar_.RemoveAll(); 89} 90 91void OmniboxMetricsProvider::ProvideGeneralMetrics( 92 metrics::ChromeUserMetricsExtension* uma_proto) { 93 uma_proto->mutable_omnibox_event()->Swap( 94 omnibox_events_cache.mutable_omnibox_event()); 95} 96 97void 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 110void 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