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