Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / app_list / win / app_list_service_win.cc
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.
4
5 #include "chrome/browser/ui/views/app_list/win/app_list_service_win.h"
6
7 #include <dwmapi.h>
8 #include <sstream>
9
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/lazy_instance.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram.h"
16 #include "base/path_service.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/sequenced_worker_pool.h"
20 #include "base/time/time.h"
21 #include "base/timer/timer.h"
22 #include "base/win/shortcut.h"
23 #include "base/win/windows_version.h"
24 #include "chrome/app/chrome_dll_resource.h"
25 #include "chrome/browser/browser_process.h"
26 #include "chrome/browser/browser_shutdown.h"
27 #include "chrome/browser/platform_util.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/profiles/profile_manager.h"
30 #include "chrome/browser/shell_integration.h"
31 #include "chrome/browser/ui/app_list/app_list.h"
32 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
33 #include "chrome/browser/ui/app_list/app_list_factory.h"
34 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
35 #include "chrome/browser/ui/app_list/app_list_shower.h"
36 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
37 #include "chrome/browser/ui/app_list/keep_alive_service_impl.h"
38 #include "chrome/browser/ui/apps/app_metro_infobar_delegate_win.h"
39 #include "chrome/browser/ui/views/app_list/win/activation_tracker_win.h"
40 #include "chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h"
41 #include "chrome/browser/ui/views/app_list/win/app_list_win.h"
42 #include "chrome/browser/web_applications/web_app.h"
43 #include "chrome/common/chrome_constants.h"
44 #include "chrome/common/chrome_switches.h"
45 #include "chrome/common/chrome_version_info.h"
46 #include "chrome/common/pref_names.h"
47 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
48 #include "chrome/installer/util/browser_distribution.h"
49 #include "chrome/installer/util/google_update_settings.h"
50 #include "chrome/installer/util/install_util.h"
51 #include "chrome/installer/util/util_constants.h"
52 #include "content/public/browser/browser_thread.h"
53 #include "grit/chromium_strings.h"
54 #include "grit/generated_resources.h"
55 #include "grit/google_chrome_strings.h"
56 #include "grit/theme_resources.h"
57 #include "ui/app_list/app_list_model.h"
58 #include "ui/app_list/pagination_model.h"
59 #include "ui/app_list/views/app_list_view.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
62 #include "ui/base/win/shell.h"
63 #include "ui/gfx/display.h"
64 #include "ui/gfx/image/image_skia.h"
65 #include "ui/gfx/screen.h"
66 #include "ui/views/bubble/bubble_border.h"
67 #include "ui/views/widget/widget.h"
68 #include "win8/util/win8_util.h"
69
70 #if defined(USE_AURA)
71 #include "ui/aura/root_window.h"
72 #include "ui/aura/window.h"
73 #endif
74
75 #if defined(USE_ASH)
76 #include "chrome/browser/ui/app_list/app_list_service_ash.h"
77 #include "chrome/browser/ui/host_desktop.h"
78 #endif
79
80 #if defined(GOOGLE_CHROME_BUILD)
81 #include "chrome/installer/util/install_util.h"
82 #endif
83
84 // static
85 AppListService* AppListService::Get(chrome::HostDesktopType desktop_type) {
86 #if defined(USE_ASH)
87   if (desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
88     return chrome::GetAppListServiceAsh();
89 #endif
90
91   return chrome::GetAppListServiceWin();
92 }
93
94 // static
95 void AppListService::InitAll(Profile* initial_profile) {
96 #if defined(USE_ASH)
97   chrome::GetAppListServiceAsh()->Init(initial_profile);
98 #endif
99   chrome::GetAppListServiceWin()->Init(initial_profile);
100 }
101
102 namespace {
103
104 // Migrate chrome::kAppLauncherIsEnabled pref to
105 // chrome::kAppLauncherHasBeenEnabled pref.
106 void MigrateAppLauncherEnabledPref() {
107   PrefService* prefs = g_browser_process->local_state();
108   if (prefs->HasPrefPath(prefs::kAppLauncherIsEnabled)) {
109     prefs->SetBoolean(prefs::kAppLauncherHasBeenEnabled,
110                       prefs->GetBoolean(prefs::kAppLauncherIsEnabled));
111     prefs->ClearPref(prefs::kAppLauncherIsEnabled);
112   }
113 }
114
115 int GetAppListIconIndex() {
116   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
117   return dist->GetIconIndex(BrowserDistribution::SHORTCUT_APP_LAUNCHER);
118 }
119
120 base::string16 GetAppListIconPath() {
121   base::FilePath icon_path;
122   if (!PathService::Get(base::FILE_EXE, &icon_path)) {
123     NOTREACHED();
124     return base::string16();
125   }
126
127   std::stringstream ss;
128   ss << "," << GetAppListIconIndex();
129   base::string16 result = icon_path.value();
130   result.append(base::UTF8ToUTF16(ss.str()));
131   return result;
132 }
133
134 base::string16 GetAppListShortcutName() {
135   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
136   return dist->GetShortcutName(BrowserDistribution::SHORTCUT_APP_LAUNCHER);
137 }
138
139 CommandLine GetAppListCommandLine() {
140   const char* const kSwitchesToCopy[] = { switches::kUserDataDir };
141   CommandLine* current = CommandLine::ForCurrentProcess();
142   base::FilePath chrome_exe;
143   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
144      NOTREACHED();
145      return CommandLine(CommandLine::NO_PROGRAM);
146   }
147   CommandLine command_line(chrome_exe);
148   command_line.CopySwitchesFrom(*current, kSwitchesToCopy,
149                                 arraysize(kSwitchesToCopy));
150   command_line.AppendSwitch(switches::kShowAppList);
151   return command_line;
152 }
153
154 base::string16 GetAppModelId() {
155   // The AppModelId should be the same for all profiles in a user data directory
156   // but different for different user data directories, so base it on the
157   // initial profile in the current user data directory.
158   base::FilePath initial_profile_path;
159   CommandLine* command_line = CommandLine::ForCurrentProcess();
160   if (command_line->HasSwitch(switches::kUserDataDir)) {
161     initial_profile_path =
162         command_line->GetSwitchValuePath(switches::kUserDataDir).AppendASCII(
163             chrome::kInitialProfile);
164   }
165   return ShellIntegration::GetAppListAppModelIdForProfile(initial_profile_path);
166 }
167
168 void SetDidRunForNDayActiveStats() {
169   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
170   base::FilePath exe_path;
171   if (!PathService::Get(base::DIR_EXE, &exe_path)) {
172     NOTREACHED();
173     return;
174   }
175   bool system_install =
176       !InstallUtil::IsPerUserInstall(exe_path.value().c_str());
177   // Using Chrome Binary dist: Chrome dist may not exist for the legacy
178   // App Launcher, and App Launcher dist may be "shadow", which does not
179   // contain the information needed to determine multi-install.
180   // Edge case involving Canary: crbug/239163.
181   BrowserDistribution* chrome_binaries_dist =
182       BrowserDistribution::GetSpecificDistribution(
183           BrowserDistribution::CHROME_BINARIES);
184   if (chrome_binaries_dist &&
185       InstallUtil::IsMultiInstall(chrome_binaries_dist, system_install)) {
186     BrowserDistribution* app_launcher_dist =
187         BrowserDistribution::GetSpecificDistribution(
188             BrowserDistribution::CHROME_APP_HOST);
189     GoogleUpdateSettings::UpdateDidRunStateForDistribution(
190         app_launcher_dist,
191         true /* did_run */,
192         system_install);
193   }
194 }
195
196 // The start menu shortcut is created on first run by users that are
197 // upgrading. The desktop and taskbar shortcuts are created the first time the
198 // user enables the app list. The taskbar shortcut is created in
199 // |user_data_dir| and will use a Windows Application Model Id of
200 // |app_model_id|. This runs on the FILE thread and not in the blocking IO
201 // thread pool as there are other tasks running (also on the FILE thread)
202 // which fiddle with shortcut icons
203 // (ShellIntegration::MigrateWin7ShortcutsOnPath). Having different threads
204 // fiddle with the same shortcuts could cause race issues.
205 void CreateAppListShortcuts(
206     const base::FilePath& user_data_dir,
207     const base::string16& app_model_id,
208     const ShellIntegration::ShortcutLocations& creation_locations) {
209   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
210
211   // Shortcut paths under which to create shortcuts.
212   std::vector<base::FilePath> shortcut_paths =
213       web_app::internals::GetShortcutPaths(creation_locations);
214
215   bool pin_to_taskbar = creation_locations.in_quick_launch_bar &&
216                         (base::win::GetVersion() >= base::win::VERSION_WIN7);
217
218   // Create a shortcut in the |user_data_dir| for taskbar pinning.
219   if (pin_to_taskbar)
220     shortcut_paths.push_back(user_data_dir);
221   bool success = true;
222
223   base::FilePath chrome_exe;
224   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
225     NOTREACHED();
226     return;
227   }
228
229   base::string16 app_list_shortcut_name = GetAppListShortcutName();
230
231   base::string16 wide_switches(GetAppListCommandLine().GetArgumentsString());
232
233   base::win::ShortcutProperties shortcut_properties;
234   shortcut_properties.set_target(chrome_exe);
235   shortcut_properties.set_working_dir(chrome_exe.DirName());
236   shortcut_properties.set_arguments(wide_switches);
237   shortcut_properties.set_description(app_list_shortcut_name);
238   shortcut_properties.set_icon(chrome_exe, GetAppListIconIndex());
239   shortcut_properties.set_app_id(app_model_id);
240
241   for (size_t i = 0; i < shortcut_paths.size(); ++i) {
242     base::FilePath shortcut_file =
243         shortcut_paths[i].Append(app_list_shortcut_name).
244             AddExtension(installer::kLnkExt);
245     if (!base::PathExists(shortcut_file.DirName()) &&
246         !base::CreateDirectory(shortcut_file.DirName())) {
247       NOTREACHED();
248       return;
249     }
250     success = success && base::win::CreateOrUpdateShortcutLink(
251         shortcut_file, shortcut_properties,
252         base::win::SHORTCUT_CREATE_ALWAYS);
253   }
254
255   if (success && pin_to_taskbar) {
256     base::FilePath shortcut_to_pin =
257         user_data_dir.Append(app_list_shortcut_name).
258             AddExtension(installer::kLnkExt);
259     success = base::win::TaskbarPinShortcutLink(
260         shortcut_to_pin.value().c_str()) && success;
261   }
262 }
263
264 // Customizes the app list |hwnd| for Windows (eg: disable aero peek, set up
265 // restart params).
266 void SetWindowAttributes(HWND hwnd) {
267   if (base::win::GetVersion() > base::win::VERSION_VISTA) {
268     // Disable aero peek. Without this, hovering over the taskbar popup puts
269     // Windows into a mode for switching between windows in the same
270     // application. The app list has just one window, so it is just distracting.
271     BOOL disable_value = TRUE;
272     ::DwmSetWindowAttribute(hwnd,
273                             DWMWA_DISALLOW_PEEK,
274                             &disable_value,
275                             sizeof(disable_value));
276   }
277
278   ui::win::SetAppIdForWindow(GetAppModelId(), hwnd);
279   CommandLine relaunch = GetAppListCommandLine();
280   base::string16 app_name(GetAppListShortcutName());
281   ui::win::SetRelaunchDetailsForWindow(
282       relaunch.GetCommandLineString(), app_name, hwnd);
283   ::SetWindowText(hwnd, app_name.c_str());
284   base::string16 icon_path = GetAppListIconPath();
285   ui::win::SetAppIconForWindow(icon_path, hwnd);
286 }
287
288 class AppListFactoryWin : public AppListFactory {
289  public:
290   explicit AppListFactoryWin(AppListServiceWin* service)
291       : service_(service) {
292   }
293
294   virtual ~AppListFactoryWin() {
295   }
296
297   virtual AppList* CreateAppList(
298       Profile* profile,
299       AppListService* service,
300       const base::Closure& on_should_dismiss) OVERRIDE {
301     // The view delegate will be owned by the app list view. The app list view
302     // manages it's own lifetime.
303     AppListViewDelegate* view_delegate =
304         new AppListViewDelegate(profile,
305                                 service->GetControllerDelegate());
306     app_list::AppListView* view = new app_list::AppListView(view_delegate);
307     gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
308     view->InitAsBubbleAtFixedLocation(NULL,
309                                       &pagination_model_,
310                                       cursor,
311                                       views::BubbleBorder::FLOAT,
312                                       false /* border_accepts_events */);
313     SetWindowAttributes(view->GetHWND());
314     return new AppListWin(view, on_should_dismiss);
315   }
316
317  private:
318   // PaginationModel that is shared across all views.
319   app_list::PaginationModel pagination_model_;
320   AppListServiceWin* service_;
321
322   DISALLOW_COPY_AND_ASSIGN(AppListFactoryWin);
323 };
324
325 }  // namespace
326
327 // static
328 AppListServiceWin* AppListServiceWin::GetInstance() {
329   return Singleton<AppListServiceWin,
330                    LeakySingletonTraits<AppListServiceWin> >::get();
331 }
332
333 AppListServiceWin::AppListServiceWin()
334     : enable_app_list_on_next_init_(false),
335       shower_(new AppListShower(
336           scoped_ptr<AppListFactory>(new AppListFactoryWin(this)),
337           scoped_ptr<KeepAliveService>(new KeepAliveServiceImpl),
338           this)),
339       controller_delegate_(new AppListControllerDelegateWin(this)),
340       weak_factory_(this) {
341 }
342
343 AppListServiceWin::~AppListServiceWin() {
344 }
345
346 void AppListServiceWin::set_can_close(bool can_close) {
347   shower_->set_can_close(can_close);
348 }
349
350 gfx::NativeWindow AppListServiceWin::GetAppListWindow() {
351   return shower_->GetWindow();
352 }
353
354 Profile* AppListServiceWin::GetCurrentAppListProfile() {
355   return shower_->profile();
356 }
357
358 AppListControllerDelegate* AppListServiceWin::GetControllerDelegate() {
359   return controller_delegate_.get();
360 }
361
362 void AppListServiceWin::ShowForProfile(Profile* requested_profile) {
363   DCHECK(requested_profile);
364   if (requested_profile->IsManaged())
365     return;
366
367   ScopedKeepAlive show_app_list_keepalive;
368
369   content::BrowserThread::PostBlockingPoolTask(
370       FROM_HERE, base::Bind(SetDidRunForNDayActiveStats));
371
372   if (win8::IsSingleWindowMetroMode()) {
373     // This request came from Windows 8 in desktop mode, but chrome is currently
374     // running in Metro mode.
375     AppMetroInfoBarDelegateWin::Create(
376         requested_profile, AppMetroInfoBarDelegateWin::SHOW_APP_LIST,
377         std::string());
378     return;
379   }
380
381   InvalidatePendingProfileLoads();
382   SetProfilePath(requested_profile->GetPath());
383   shower_->ShowForProfile(requested_profile);
384   RecordAppListLaunch();
385 }
386
387 void AppListServiceWin::DismissAppList() {
388   shower_->DismissAppList();
389 }
390
391 void AppListServiceWin::OnAppListClosing() {
392   shower_->CloseAppList();
393 }
394
395 void AppListServiceWin::OnLoadProfileForWarmup(Profile* initial_profile) {
396   if (!IsWarmupNeeded())
397     return;
398
399   base::Time before_warmup(base::Time::Now());
400   shower_->WarmupForProfile(initial_profile);
401   UMA_HISTOGRAM_TIMES("Apps.AppListWarmupDuration",
402                       base::Time::Now() - before_warmup);
403 }
404
405 void AppListServiceWin::SetAppListNextPaintCallback(void (*callback)()) {
406   app_list::AppListView::SetNextPaintCallback(callback);
407 }
408
409 void AppListServiceWin::HandleFirstRun() {
410   PrefService* local_state = g_browser_process->local_state();
411   // If the app list is already enabled during first run, then the user had
412   // opted in to the app launcher before uninstalling, so we re-enable to
413   // restore shortcuts to the app list.
414   // Note we can't directly create the shortcuts here because the IO thread
415   // hasn't been created yet.
416   enable_app_list_on_next_init_ = local_state->GetBoolean(
417       prefs::kAppLauncherHasBeenEnabled);
418 }
419
420 void AppListServiceWin::Init(Profile* initial_profile) {
421   // In non-Ash metro mode, we can not show the app list for this process, so do
422   // not bother performing Init tasks.
423   if (win8::IsSingleWindowMetroMode())
424     return;
425
426   if (enable_app_list_on_next_init_) {
427     enable_app_list_on_next_init_ = false;
428     EnableAppList(initial_profile, ENABLE_ON_REINSTALL);
429     CreateShortcut();
430   }
431
432   PrefService* prefs = g_browser_process->local_state();
433   if (prefs->HasPrefPath(prefs::kRestartWithAppList) &&
434       prefs->GetBoolean(prefs::kRestartWithAppList)) {
435     prefs->SetBoolean(prefs::kRestartWithAppList, false);
436     // If we are restarting in Metro mode we will lose focus straight away. We
437     // need to reacquire focus when that happens.
438     shower_->ShowAndReacquireFocus(initial_profile);
439   }
440
441   // Migrate from legacy app launcher if we are on a non-canary and non-chromium
442   // build.
443 #if defined(GOOGLE_CHROME_BUILD)
444   if (!InstallUtil::IsChromeSxSProcess() &&
445       !chrome_launcher_support::GetAnyAppHostPath().empty()) {
446     chrome_launcher_support::InstallationState state =
447         chrome_launcher_support::GetAppLauncherInstallationState();
448     if (state == chrome_launcher_support::NOT_INSTALLED) {
449       // If app_host.exe is found but can't be located in the registry,
450       // skip the migration as this is likely a developer build.
451       return;
452     } else if (state == chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL) {
453       chrome_launcher_support::UninstallLegacyAppLauncher(
454           chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION);
455     } else if (state == chrome_launcher_support::INSTALLED_AT_USER_LEVEL) {
456       chrome_launcher_support::UninstallLegacyAppLauncher(
457           chrome_launcher_support::USER_LEVEL_INSTALLATION);
458     }
459     EnableAppList(initial_profile, ENABLE_ON_REINSTALL);
460     CreateShortcut();
461   }
462 #endif
463
464   ScheduleWarmup();
465
466   MigrateAppLauncherEnabledPref();
467   PerformStartupChecks(initial_profile);
468 }
469
470 void AppListServiceWin::CreateForProfile(Profile* profile) {
471   shower_->CreateViewForProfile(profile);
472 }
473
474 bool AppListServiceWin::IsAppListVisible() const {
475   return shower_->IsAppListVisible();
476 }
477
478 void AppListServiceWin::CreateShortcut() {
479   // Check if the app launcher shortcuts have ever been created before.
480   // Shortcuts should only be created once. If the user unpins the taskbar
481   // shortcut, they can restore it by pinning the start menu or desktop
482   // shortcut.
483   ShellIntegration::ShortcutLocations shortcut_locations;
484   shortcut_locations.on_desktop = true;
485   shortcut_locations.in_quick_launch_bar = true;
486   shortcut_locations.applications_menu_location =
487       ShellIntegration::APP_MENU_LOCATION_SUBDIR_CHROME;
488   base::FilePath user_data_dir(
489       g_browser_process->profile_manager()->user_data_dir());
490
491   content::BrowserThread::PostTask(
492       content::BrowserThread::FILE,
493       FROM_HERE,
494       base::Bind(&CreateAppListShortcuts,
495                  user_data_dir, GetAppModelId(), shortcut_locations));
496 }
497
498 void AppListServiceWin::ScheduleWarmup() {
499   // Post a task to create the app list. This is posted to not impact startup
500   // time.
501   const int kInitWindowDelay = 30;
502   base::MessageLoop::current()->PostDelayedTask(
503       FROM_HERE,
504       base::Bind(&AppListServiceWin::LoadProfileForWarmup,
505                  weak_factory_.GetWeakPtr()),
506       base::TimeDelta::FromSeconds(kInitWindowDelay));
507 }
508
509 bool AppListServiceWin::IsWarmupNeeded() {
510   if (!g_browser_process || g_browser_process->IsShuttingDown() ||
511       browser_shutdown::IsTryingToQuit()) {
512     return false;
513   }
514
515   // We only need to initialize the view if there's no view already created and
516   // there's no profile loading to be shown.
517   return !shower_->HasView() && !profile_loader().IsAnyProfileLoading();
518 }
519
520 void AppListServiceWin::LoadProfileForWarmup() {
521   if (!IsWarmupNeeded())
522     return;
523
524   ProfileManager* profile_manager = g_browser_process->profile_manager();
525   base::FilePath profile_path(GetProfilePath(profile_manager->user_data_dir()));
526
527   profile_loader().LoadProfileInvalidatingOtherLoads(
528       profile_path,
529       base::Bind(&AppListServiceWin::OnLoadProfileForWarmup,
530                  weak_factory_.GetWeakPtr()));
531 }
532
533 namespace chrome {
534
535 AppListService* GetAppListServiceWin() {
536   return AppListServiceWin::GetInstance();
537 }
538
539 }  // namespace chrome