Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / ui / app_list / app_list_service_impl.cc
bloba388c543e4eda2e67551c00940e29763cf7fb968
1 // Copyright 2013 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/ui/app_list/app_list_service_impl.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string16.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/browser_shutdown.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
19 #include "chrome/browser/ui/app_list/profile_loader.h"
20 #include "chrome/browser/ui/app_list/profile_store.h"
21 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/pref_names.h"
24 #include "ui/app_list/app_list_model.h"
26 namespace {
28 const int kDiscoverabilityTimeoutMinutes = 60;
30 void SendAppListAppLaunch(int count) {
31 UMA_HISTOGRAM_CUSTOM_COUNTS(
32 "Apps.AppListDailyAppLaunches", count, 1, 1000, 50);
33 if (count > 0)
34 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppToday", 1, 2);
37 void SendAppListLaunch(int count) {
38 UMA_HISTOGRAM_CUSTOM_COUNTS(
39 "Apps.AppListDailyLaunches", count, 1, 1000, 50);
40 if (count > 0)
41 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHasLaunchedAppListToday", 1, 2);
44 bool SendDailyEventFrequency(
45 const char* last_ping_pref,
46 const char* count_pref,
47 void (*send_callback)(int count)) {
48 PrefService* local_state = g_browser_process->local_state();
50 base::Time now = base::Time::Now();
51 base::Time last = base::Time::FromInternalValue(local_state->GetInt64(
52 last_ping_pref));
53 int days = (now - last).InDays();
54 if (days > 0) {
55 send_callback(local_state->GetInteger(count_pref));
56 local_state->SetInt64(
57 last_ping_pref,
58 (last + base::TimeDelta::FromDays(days)).ToInternalValue());
59 local_state->SetInteger(count_pref, 0);
60 return true;
62 return false;
65 void RecordDailyEventFrequency(
66 const char* last_ping_pref,
67 const char* count_pref,
68 void (*send_callback)(int count)) {
69 if (!g_browser_process)
70 return; // In a unit test.
72 PrefService* local_state = g_browser_process->local_state();
73 if (!local_state)
74 return; // In a unit test.
76 int count = local_state->GetInteger(count_pref);
77 local_state->SetInteger(count_pref, count + 1);
78 if (SendDailyEventFrequency(last_ping_pref, count_pref, send_callback)) {
79 local_state->SetInteger(count_pref, 1);
83 class ProfileStoreImpl : public ProfileStore {
84 public:
85 explicit ProfileStoreImpl(ProfileManager* profile_manager)
86 : profile_manager_(profile_manager),
87 weak_factory_(this) {
90 virtual void AddProfileObserver(ProfileInfoCacheObserver* observer) override {
91 profile_manager_->GetProfileInfoCache().AddObserver(observer);
94 virtual void LoadProfileAsync(
95 const base::FilePath& path,
96 base::Callback<void(Profile*)> callback) override {
97 profile_manager_->CreateProfileAsync(
98 path,
99 base::Bind(&ProfileStoreImpl::OnProfileCreated,
100 weak_factory_.GetWeakPtr(),
101 callback),
102 base::string16(),
103 base::string16(),
104 std::string());
107 void OnProfileCreated(base::Callback<void(Profile*)> callback,
108 Profile* profile,
109 Profile::CreateStatus status) {
110 switch (status) {
111 case Profile::CREATE_STATUS_CREATED:
112 break;
113 case Profile::CREATE_STATUS_INITIALIZED:
114 callback.Run(profile);
115 break;
116 case Profile::CREATE_STATUS_LOCAL_FAIL:
117 case Profile::CREATE_STATUS_REMOTE_FAIL:
118 case Profile::CREATE_STATUS_CANCELED:
119 break;
120 case Profile::MAX_CREATE_STATUS:
121 NOTREACHED();
122 break;
126 virtual Profile* GetProfileByPath(const base::FilePath& path) override {
127 return profile_manager_->GetProfileByPath(path);
130 virtual base::FilePath GetUserDataDir() override {
131 return profile_manager_->user_data_dir();
134 virtual bool IsProfileSupervised(
135 const base::FilePath& profile_path) override {
136 ProfileInfoCache& profile_info =
137 g_browser_process->profile_manager()->GetProfileInfoCache();
138 size_t profile_index = profile_info.GetIndexOfProfileWithPath(profile_path);
139 return profile_index != std::string::npos &&
140 profile_info.ProfileIsSupervisedAtIndex(profile_index);
143 private:
144 ProfileManager* profile_manager_;
145 base::WeakPtrFactory<ProfileStoreImpl> weak_factory_;
148 void RecordAppListDiscoverability(PrefService* local_state,
149 bool is_startup_check) {
150 // Since this task may be delayed, ensure it does not interfere with shutdown
151 // when they unluckily coincide.
152 if (browser_shutdown::IsTryingToQuit())
153 return;
155 int64 enable_time_value = local_state->GetInt64(prefs::kAppListEnableTime);
156 if (enable_time_value == 0)
157 return; // Already recorded or never enabled.
159 base::Time app_list_enable_time =
160 base::Time::FromInternalValue(enable_time_value);
161 if (is_startup_check) {
162 // When checking at startup, only clear and record the "timeout" case,
163 // otherwise wait for a timeout.
164 base::TimeDelta time_remaining =
165 app_list_enable_time +
166 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes) -
167 base::Time::Now();
168 if (time_remaining > base::TimeDelta()) {
169 base::MessageLoop::current()->PostDelayedTask(
170 FROM_HERE,
171 base::Bind(&RecordAppListDiscoverability,
172 base::Unretained(local_state),
173 false),
174 time_remaining);
175 return;
179 local_state->SetInt64(prefs::kAppListEnableTime, 0);
181 AppListService::AppListEnableSource enable_source =
182 static_cast<AppListService::AppListEnableSource>(
183 local_state->GetInteger(prefs::kAppListEnableMethod));
184 if (enable_source == AppListService::ENABLE_FOR_APP_INSTALL) {
185 base::TimeDelta time_taken = base::Time::Now() - app_list_enable_time;
186 // This means the user "discovered" the app launcher naturally, after it was
187 // enabled on the first app install. Record how long it took to discover.
188 // Note that the last bucket is essentially "not discovered": subtract 1
189 // minute to account for clock inaccuracy.
190 UMA_HISTOGRAM_CUSTOM_TIMES(
191 "Apps.AppListTimeToDiscover",
192 time_taken,
193 base::TimeDelta::FromSeconds(1),
194 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes - 1),
195 10 /* bucket_count */);
197 UMA_HISTOGRAM_ENUMERATION("Apps.AppListHowEnabled",
198 enable_source,
199 AppListService::ENABLE_NUM_ENABLE_SOURCES);
202 } // namespace
204 void AppListServiceImpl::RecordAppListLaunch() {
205 RecordDailyEventFrequency(prefs::kLastAppListLaunchPing,
206 prefs::kAppListLaunchCount,
207 &SendAppListLaunch);
208 RecordAppListDiscoverability(local_state_, false);
211 // static
212 void AppListServiceImpl::RecordAppListAppLaunch() {
213 RecordDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
214 prefs::kAppListAppLaunchCount,
215 &SendAppListAppLaunch);
218 // static
219 void AppListServiceImpl::SendAppListStats() {
220 if (!g_browser_process || g_browser_process->IsShuttingDown())
221 return;
223 SendDailyEventFrequency(prefs::kLastAppListLaunchPing,
224 prefs::kAppListLaunchCount,
225 &SendAppListLaunch);
226 SendDailyEventFrequency(prefs::kLastAppListAppLaunchPing,
227 prefs::kAppListAppLaunchCount,
228 &SendAppListAppLaunch);
231 AppListServiceImpl::AppListServiceImpl()
232 : profile_store_(
233 new ProfileStoreImpl(g_browser_process->profile_manager())),
234 command_line_(*CommandLine::ForCurrentProcess()),
235 local_state_(g_browser_process->local_state()),
236 profile_loader_(new ProfileLoader(profile_store_.get())),
237 weak_factory_(this) {
238 profile_store_->AddProfileObserver(this);
241 AppListServiceImpl::AppListServiceImpl(const CommandLine& command_line,
242 PrefService* local_state,
243 scoped_ptr<ProfileStore> profile_store)
244 : profile_store_(profile_store.Pass()),
245 command_line_(command_line),
246 local_state_(local_state),
247 profile_loader_(new ProfileLoader(profile_store_.get())),
248 weak_factory_(this) {
249 profile_store_->AddProfileObserver(this);
252 AppListServiceImpl::~AppListServiceImpl() {}
254 AppListViewDelegate* AppListServiceImpl::GetViewDelegate(Profile* profile) {
255 if (!view_delegate_)
256 view_delegate_.reset(new AppListViewDelegate(GetControllerDelegate()));
257 view_delegate_->SetProfile(profile);
258 return view_delegate_.get();
261 void AppListServiceImpl::SetAppListNextPaintCallback(void (*callback)()) {}
263 void AppListServiceImpl::HandleFirstRun() {}
265 void AppListServiceImpl::Init(Profile* initial_profile) {}
267 base::FilePath AppListServiceImpl::GetProfilePath(
268 const base::FilePath& user_data_dir) {
269 std::string app_list_profile;
270 if (local_state_->HasPrefPath(prefs::kAppListProfile))
271 app_list_profile = local_state_->GetString(prefs::kAppListProfile);
273 // If the user has no profile preference for the app launcher, default to the
274 // last browser profile used.
275 if (app_list_profile.empty() &&
276 local_state_->HasPrefPath(prefs::kProfileLastUsed)) {
277 app_list_profile = local_state_->GetString(prefs::kProfileLastUsed);
280 // If there is no last used profile recorded, use the initial profile.
281 if (app_list_profile.empty())
282 app_list_profile = chrome::kInitialProfile;
284 return user_data_dir.AppendASCII(app_list_profile);
287 void AppListServiceImpl::SetProfilePath(const base::FilePath& profile_path) {
288 local_state_->SetString(
289 prefs::kAppListProfile,
290 profile_path.BaseName().MaybeAsASCII());
293 void AppListServiceImpl::CreateShortcut() {}
295 void AppListServiceImpl::OnProfileWillBeRemoved(
296 const base::FilePath& profile_path) {
297 // We need to watch for profile removal to keep kAppListProfile updated, for
298 // the case that the deleted profile is being used by the app list.
299 std::string app_list_last_profile = local_state_->GetString(
300 prefs::kAppListProfile);
301 if (profile_path.BaseName().MaybeAsASCII() != app_list_last_profile)
302 return;
304 // Switch the app list over to a valid profile.
305 // Before ProfileInfoCache::DeleteProfileFromCache() calls this function,
306 // ProfileManager::ScheduleProfileForDeletion() will have checked to see if
307 // the deleted profile was also "last used", and updated that setting with
308 // something valid.
309 local_state_->SetString(prefs::kAppListProfile,
310 local_state_->GetString(prefs::kProfileLastUsed));
312 // If the app list was never shown, there won't be a |view_delegate_| yet.
313 if (!view_delegate_)
314 return;
316 // The Chrome AppListViewDelegate now needs its profile cleared, because:
317 // 1. it has many references to the profile and can't be profile-keyed, and
318 // 2. the last used profile might not be loaded yet.
319 // - this loading is sometimes done by the ProfileManager asynchronously,
320 // so the app list can't just switch to that.
321 // Only Mac supports showing the app list with a NULL profile, so tear down
322 // the view.
323 DestroyAppList();
324 view_delegate_->SetProfile(NULL);
327 void AppListServiceImpl::Show() {
328 profile_loader_->LoadProfileInvalidatingOtherLoads(
329 GetProfilePath(profile_store_->GetUserDataDir()),
330 base::Bind(&AppListServiceImpl::ShowForProfile,
331 weak_factory_.GetWeakPtr()));
334 void AppListServiceImpl::ShowForVoiceSearch(Profile* profile) {
335 ShowForProfile(profile);
336 view_delegate_->ToggleSpeechRecognition();
339 void AppListServiceImpl::ShowForAppInstall(Profile* profile,
340 const std::string& extension_id,
341 bool start_discovery_tracking) {
342 if (start_discovery_tracking) {
343 CreateForProfile(profile);
344 } else {
345 // Check if the app launcher has not yet been shown ever. Since this will
346 // show it, if discoverability UMA hasn't yet been recorded, it needs to be
347 // counted as undiscovered.
348 if (local_state_->GetInt64(prefs::kAppListEnableTime) != 0) {
349 local_state_->SetInteger(prefs::kAppListEnableMethod,
350 ENABLE_SHOWN_UNDISCOVERED);
352 ShowForProfile(profile);
354 if (extension_id.empty())
355 return; // Nothing to highlight. Only used in tests.
357 // The only way an install can happen is with the profile already loaded. So,
358 // ShowForProfile() can never be asynchronous, and the model is guaranteed to
359 // exist after a show.
360 DCHECK(view_delegate_->GetModel());
361 view_delegate_->GetModel()
362 ->top_level_item_list()
363 ->HighlightItemInstalledFromUI(extension_id);
366 void AppListServiceImpl::EnableAppList(Profile* initial_profile,
367 AppListEnableSource enable_source) {
368 SetProfilePath(initial_profile->GetPath());
369 // Always allow the webstore "enable" button to re-run the install flow.
370 if (enable_source != AppListService::ENABLE_VIA_WEBSTORE_LINK &&
371 local_state_->GetBoolean(prefs::kAppLauncherHasBeenEnabled)) {
372 return;
375 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, true);
376 CreateShortcut();
378 // UMA for launcher discoverability.
379 local_state_->SetInt64(prefs::kAppListEnableTime,
380 base::Time::Now().ToInternalValue());
381 local_state_->SetInteger(prefs::kAppListEnableMethod, enable_source);
382 if (base::MessageLoop::current()) {
383 // Ensure a value is recorded if the user "never" shows the app list. Note
384 // there is no message loop in unit tests.
385 base::MessageLoop::current()->PostDelayedTask(
386 FROM_HERE,
387 base::Bind(&RecordAppListDiscoverability,
388 base::Unretained(local_state_),
389 false),
390 base::TimeDelta::FromMinutes(kDiscoverabilityTimeoutMinutes));
394 void AppListServiceImpl::InvalidatePendingProfileLoads() {
395 profile_loader_->InvalidatePendingProfileLoads();
398 void AppListServiceImpl::PerformStartupChecks(Profile* initial_profile) {
399 // Except in rare, once-off cases, this just checks that a pref is "0" and
400 // returns.
401 RecordAppListDiscoverability(local_state_, true);
403 if (command_line_.HasSwitch(switches::kResetAppListInstallState))
404 local_state_->SetBoolean(prefs::kAppLauncherHasBeenEnabled, false);
406 if (command_line_.HasSwitch(switches::kEnableAppList))
407 EnableAppList(initial_profile, ENABLE_VIA_COMMAND_LINE);
409 if (!base::MessageLoop::current())
410 return; // In a unit test.
412 // Send app list usage stats after a delay.
413 const int kSendUsageStatsDelay = 5;
414 base::MessageLoop::current()->PostDelayedTask(
415 FROM_HERE,
416 base::Bind(&AppListServiceImpl::SendAppListStats),
417 base::TimeDelta::FromSeconds(kSendUsageStatsDelay));