Home | History | Annotate | Download | only in app_list
      1 // Copyright (c) 2012 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 <dwmapi.h>
      6 #include <sstream>
      7 
      8 #include "apps/pref_names.h"
      9 #include "base/command_line.h"
     10 #include "base/file_util.h"
     11 #include "base/lazy_instance.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/memory/weak_ptr.h"
     14 #include "base/path_service.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/threading/sequenced_worker_pool.h"
     18 #include "base/time/time.h"
     19 #include "base/timer/timer.h"
     20 #include "base/win/shortcut.h"
     21 #include "base/win/windows_version.h"
     22 #include "chrome/app/chrome_dll_resource.h"
     23 #include "chrome/browser/browser_process.h"
     24 #include "chrome/browser/extensions/extension_service.h"
     25 #include "chrome/browser/extensions/extension_system.h"
     26 #include "chrome/browser/lifetime/application_lifetime.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_controller_delegate.h"
     32 #include "chrome/browser/ui/app_list/app_list_service_impl.h"
     33 #include "chrome/browser/ui/app_list/app_list_service_win.h"
     34 #include "chrome/browser/ui/app_list/app_list_view_delegate.h"
     35 #include "chrome/browser/ui/browser_commands.h"
     36 #include "chrome/browser/ui/extensions/app_metro_infobar_delegate_win.h"
     37 #include "chrome/browser/ui/extensions/application_launch.h"
     38 #include "chrome/browser/ui/views/browser_dialogs.h"
     39 #include "chrome/browser/web_applications/web_app.h"
     40 #include "chrome/common/chrome_constants.h"
     41 #include "chrome/common/chrome_switches.h"
     42 #include "chrome/common/chrome_version_info.h"
     43 #include "chrome/common/pref_names.h"
     44 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
     45 #include "chrome/installer/util/browser_distribution.h"
     46 #include "chrome/installer/util/google_update_settings.h"
     47 #include "chrome/installer/util/install_util.h"
     48 #include "chrome/installer/util/util_constants.h"
     49 #include "content/public/browser/browser_thread.h"
     50 #include "grit/chromium_strings.h"
     51 #include "grit/generated_resources.h"
     52 #include "grit/google_chrome_strings.h"
     53 #include "ui/app_list/pagination_model.h"
     54 #include "ui/app_list/views/app_list_view.h"
     55 #include "ui/base/l10n/l10n_util.h"
     56 #include "ui/base/resource/resource_bundle.h"
     57 #include "ui/base/win/shell.h"
     58 #include "ui/gfx/display.h"
     59 #include "ui/gfx/image/image_skia.h"
     60 #include "ui/gfx/screen.h"
     61 #include "ui/views/bubble/bubble_border.h"
     62 #include "ui/views/widget/widget.h"
     63 #include "win8/util/win8_util.h"
     64 
     65 #if defined(GOOGLE_CHROME_BUILD)
     66 #include "chrome/installer/util/install_util.h"
     67 #endif
     68 
     69 #if defined(USE_AURA)
     70 #include "ui/aura/root_window.h"
     71 #include "ui/aura/window.h"
     72 #endif
     73 
     74 namespace {
     75 
     76 // Offset from the cursor to the point of the bubble arrow. It looks weird
     77 // if the arrow comes up right on top of the cursor, so it is offset by this
     78 // amount.
     79 static const int kAnchorOffset = 25;
     80 
     81 static const wchar_t kTrayClassName[] = L"Shell_TrayWnd";
     82 
     83 // Migrate chrome::kAppLauncherIsEnabled pref to
     84 // chrome::kAppLauncherHasBeenEnabled pref.
     85 void MigrateAppLauncherEnabledPref() {
     86   PrefService* prefs = g_browser_process->local_state();
     87   if (prefs->HasPrefPath(apps::prefs::kAppLauncherIsEnabled)) {
     88     prefs->SetBoolean(apps::prefs::kAppLauncherHasBeenEnabled,
     89                       prefs->GetBoolean(apps::prefs::kAppLauncherIsEnabled));
     90     prefs->ClearPref(apps::prefs::kAppLauncherIsEnabled);
     91   }
     92 }
     93 
     94 // Icons are added to the resources of the DLL using icon names. The icon index
     95 // for the app list icon is named IDR_X_APP_LIST or (for official builds)
     96 // IDR_X_APP_LIST_SXS for Chrome Canary. Creating shortcuts needs to specify a
     97 // resource index, which are different to icon names.  They are 0 based and
     98 // contiguous. As Google Chrome builds have extra icons the icon for Google
     99 // Chrome builds need to be higher. Unfortunately these indexes are not in any
    100 // generated header file.
    101 int GetAppListIconIndex() {
    102   const int kAppListIconIndex = 5;
    103   const int kAppListIconIndexSxS = 6;
    104   const int kAppListIconIndexChromium = 1;
    105 #if defined(GOOGLE_CHROME_BUILD)
    106   if (InstallUtil::IsChromeSxSProcess())
    107     return kAppListIconIndexSxS;
    108   return kAppListIconIndex;
    109 #else
    110   return kAppListIconIndexChromium;
    111 #endif
    112 }
    113 
    114 string16 GetAppListShortcutName() {
    115   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    116   if (channel == chrome::VersionInfo::CHANNEL_CANARY)
    117     return l10n_util::GetStringUTF16(IDS_APP_LIST_SHORTCUT_NAME_CANARY);
    118   return l10n_util::GetStringUTF16(IDS_APP_LIST_SHORTCUT_NAME);
    119 }
    120 
    121 CommandLine GetAppListCommandLine() {
    122   const char* const kSwitchesToCopy[] = { switches::kUserDataDir };
    123   CommandLine* current = CommandLine::ForCurrentProcess();
    124   base::FilePath chrome_exe;
    125   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
    126      NOTREACHED();
    127      return CommandLine(CommandLine::NO_PROGRAM);
    128   }
    129   CommandLine command_line(chrome_exe);
    130   command_line.CopySwitchesFrom(*current, kSwitchesToCopy,
    131                                 arraysize(kSwitchesToCopy));
    132   command_line.AppendSwitch(switches::kShowAppList);
    133   return command_line;
    134 }
    135 
    136 string16 GetAppModelId() {
    137   // The AppModelId should be the same for all profiles in a user data directory
    138   // but different for different user data directories, so base it on the
    139   // initial profile in the current user data directory.
    140   base::FilePath initial_profile_path;
    141   CommandLine* command_line = CommandLine::ForCurrentProcess();
    142   if (command_line->HasSwitch(switches::kUserDataDir)) {
    143     initial_profile_path =
    144         command_line->GetSwitchValuePath(switches::kUserDataDir).AppendASCII(
    145             chrome::kInitialProfile);
    146   }
    147   return ShellIntegration::GetAppListAppModelIdForProfile(initial_profile_path);
    148 }
    149 
    150 void SetDidRunForNDayActiveStats() {
    151   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
    152   base::FilePath exe_path;
    153   if (!PathService::Get(base::DIR_EXE, &exe_path)) {
    154     NOTREACHED();
    155     return;
    156   }
    157   bool system_install =
    158       !InstallUtil::IsPerUserInstall(exe_path.value().c_str());
    159   // Using Chrome Binary dist: Chrome dist may not exist for the legacy
    160   // App Launcher, and App Launcher dist may be "shadow", which does not
    161   // contain the information needed to determine multi-install.
    162   // Edge case involving Canary: crbug/239163.
    163   BrowserDistribution* chrome_binaries_dist =
    164       BrowserDistribution::GetSpecificDistribution(
    165           BrowserDistribution::CHROME_BINARIES);
    166   if (chrome_binaries_dist &&
    167       InstallUtil::IsMultiInstall(chrome_binaries_dist, system_install)) {
    168     BrowserDistribution* app_launcher_dist =
    169         BrowserDistribution::GetSpecificDistribution(
    170             BrowserDistribution::CHROME_APP_HOST);
    171     GoogleUpdateSettings::UpdateDidRunStateForDistribution(
    172         app_launcher_dist,
    173         true /* did_run */,
    174         system_install);
    175   }
    176 }
    177 
    178 // The start menu shortcut is created on first run by users that are
    179 // upgrading. The desktop and taskbar shortcuts are created the first time the
    180 // user enables the app list. The taskbar shortcut is created in
    181 // |user_data_dir| and will use a Windows Application Model Id of
    182 // |app_model_id|. This runs on the FILE thread and not in the blocking IO
    183 // thread pool as there are other tasks running (also on the FILE thread)
    184 // which fiddle with shortcut icons
    185 // (ShellIntegration::MigrateWin7ShortcutsOnPath). Having different threads
    186 // fiddle with the same shortcuts could cause race issues.
    187 void CreateAppListShortcuts(
    188     const base::FilePath& user_data_dir,
    189     const string16& app_model_id,
    190     const ShellIntegration::ShortcutLocations& creation_locations) {
    191   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    192 
    193   // Shortcut paths under which to create shortcuts.
    194   std::vector<base::FilePath> shortcut_paths =
    195       web_app::internals::GetShortcutPaths(creation_locations);
    196 
    197   bool pin_to_taskbar = creation_locations.in_quick_launch_bar &&
    198                         (base::win::GetVersion() >= base::win::VERSION_WIN7);
    199 
    200   // Create a shortcut in the |user_data_dir| for taskbar pinning.
    201   if (pin_to_taskbar)
    202     shortcut_paths.push_back(user_data_dir);
    203   bool success = true;
    204 
    205   base::FilePath chrome_exe;
    206   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
    207     NOTREACHED();
    208     return;
    209   }
    210 
    211   string16 app_list_shortcut_name = GetAppListShortcutName();
    212 
    213   string16 wide_switches(GetAppListCommandLine().GetArgumentsString());
    214 
    215   base::win::ShortcutProperties shortcut_properties;
    216   shortcut_properties.set_target(chrome_exe);
    217   shortcut_properties.set_working_dir(chrome_exe.DirName());
    218   shortcut_properties.set_arguments(wide_switches);
    219   shortcut_properties.set_description(app_list_shortcut_name);
    220   shortcut_properties.set_icon(chrome_exe, GetAppListIconIndex());
    221   shortcut_properties.set_app_id(app_model_id);
    222 
    223   for (size_t i = 0; i < shortcut_paths.size(); ++i) {
    224     base::FilePath shortcut_file =
    225         shortcut_paths[i].Append(app_list_shortcut_name).
    226             AddExtension(installer::kLnkExt);
    227     if (!base::PathExists(shortcut_file.DirName()) &&
    228         !file_util::CreateDirectory(shortcut_file.DirName())) {
    229       NOTREACHED();
    230       return;
    231     }
    232     success = success && base::win::CreateOrUpdateShortcutLink(
    233         shortcut_file, shortcut_properties,
    234         base::win::SHORTCUT_CREATE_ALWAYS);
    235   }
    236 
    237   if (success && pin_to_taskbar) {
    238     base::FilePath shortcut_to_pin =
    239         user_data_dir.Append(app_list_shortcut_name).
    240             AddExtension(installer::kLnkExt);
    241     success = base::win::TaskbarPinShortcutLink(
    242         shortcut_to_pin.value().c_str()) && success;
    243   }
    244 }
    245 
    246 class AppListControllerDelegateWin : public AppListControllerDelegate {
    247  public:
    248   AppListControllerDelegateWin();
    249   virtual ~AppListControllerDelegateWin();
    250 
    251  private:
    252   // AppListController overrides:
    253   virtual void DismissView() OVERRIDE;
    254   virtual void ViewClosing() OVERRIDE;
    255   virtual void ViewActivationChanged(bool active) OVERRIDE;
    256   virtual gfx::NativeWindow GetAppListWindow() OVERRIDE;
    257   virtual gfx::ImageSkia GetWindowIcon() OVERRIDE;
    258   virtual bool CanPin() OVERRIDE;
    259   virtual void OnShowExtensionPrompt() OVERRIDE;
    260   virtual void OnCloseExtensionPrompt() OVERRIDE;
    261   virtual bool CanDoCreateShortcutsFlow(bool is_platform_app) OVERRIDE;
    262   virtual void DoCreateShortcutsFlow(Profile* profile,
    263                                      const std::string& extension_id) OVERRIDE;
    264   virtual void CreateNewWindow(Profile* profile, bool incognito) OVERRIDE;
    265   virtual void ActivateApp(Profile* profile,
    266                            const extensions::Extension* extension,
    267                            int event_flags) OVERRIDE;
    268   virtual void LaunchApp(Profile* profile,
    269                          const extensions::Extension* extension,
    270                          int event_flags) OVERRIDE;
    271 
    272   DISALLOW_COPY_AND_ASSIGN(AppListControllerDelegateWin);
    273 };
    274 
    275 class ScopedKeepAlive {
    276  public:
    277   ScopedKeepAlive() { chrome::StartKeepAlive(); }
    278   ~ScopedKeepAlive() { chrome::EndKeepAlive(); }
    279 
    280  private:
    281   DISALLOW_COPY_AND_ASSIGN(ScopedKeepAlive);
    282 };
    283 
    284 class ActivationTracker {
    285  public:
    286   ActivationTracker(app_list::AppListView* view,
    287                     const base::Closure& on_active_lost)
    288       : view_(view),
    289         on_active_lost_(on_active_lost),
    290         regain_next_lost_focus_(false),
    291         preserving_focus_for_taskbar_menu_(false) {
    292   }
    293 
    294   void RegainNextLostFocus() {
    295     regain_next_lost_focus_ = true;
    296   }
    297 
    298   void OnActivationChanged(bool active) {
    299     const int kFocusCheckIntervalMS = 250;
    300     if (active) {
    301       timer_.Stop();
    302       return;
    303     }
    304 
    305     preserving_focus_for_taskbar_menu_ = false;
    306     timer_.Start(FROM_HERE,
    307                  base::TimeDelta::FromMilliseconds(kFocusCheckIntervalMS), this,
    308                  &ActivationTracker::CheckTaskbarOrViewHasFocus);
    309   }
    310 
    311   void OnViewHidden() {
    312     timer_.Stop();
    313   }
    314 
    315   void CheckTaskbarOrViewHasFocus() {
    316     // Remember if the taskbar had focus without the right mouse button being
    317     // down.
    318     bool was_preserving_focus = preserving_focus_for_taskbar_menu_;
    319     preserving_focus_for_taskbar_menu_ = false;
    320 
    321     // First get the taskbar and jump lists windows (the jump list is the
    322     // context menu which the taskbar uses).
    323     HWND jump_list_hwnd = FindWindow(L"DV2ControlHost", NULL);
    324     HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL);
    325 
    326     // This code is designed to hide the app launcher when it loses focus,
    327     // except for the cases necessary to allow the launcher to be pinned or
    328     // closed via the taskbar context menu.
    329     // First work out if the left or right button is currently down.
    330     int swapped = GetSystemMetrics(SM_SWAPBUTTON);
    331     int left_button = swapped ? VK_RBUTTON : VK_LBUTTON;
    332     bool left_button_down = GetAsyncKeyState(left_button) < 0;
    333     int right_button = swapped ? VK_LBUTTON : VK_RBUTTON;
    334     bool right_button_down = GetAsyncKeyState(right_button) < 0;
    335 
    336     // Now get the window that currently has focus.
    337     HWND focused_hwnd = GetForegroundWindow();
    338     if (!focused_hwnd) {
    339       // Sometimes the focused window is NULL. This can happen when the focus is
    340       // changing due to a mouse button press. If the button is still being
    341       // pressed the launcher should not be hidden.
    342       if (right_button_down || left_button_down)
    343         return;
    344 
    345       // If the focused window is NULL, and the mouse button is not being
    346       // pressed, then the launcher no longer has focus.
    347       on_active_lost_.Run();
    348       return;
    349     }
    350 
    351     while (focused_hwnd) {
    352       // If the focused window is the right click menu (called a jump list) or
    353       // the app list, don't hide the launcher.
    354       if (focused_hwnd == jump_list_hwnd ||
    355           focused_hwnd == view_->GetHWND()) {
    356         return;
    357       }
    358 
    359       if (focused_hwnd == taskbar_hwnd) {
    360         // If the focused window is the taskbar, and the right button is down,
    361         // don't hide the launcher as the user might be bringing up the menu.
    362         if (right_button_down)
    363           return;
    364 
    365         // There is a short period between the right mouse button being down
    366         // and the menu gaining focus, where the taskbar has focus and no button
    367         // is down. If the taskbar is observed in this state once the launcher
    368         // is not dismissed. If it happens twice in a row it is dismissed.
    369         if (!was_preserving_focus) {
    370           preserving_focus_for_taskbar_menu_ = true;
    371           return;
    372         }
    373 
    374         break;
    375       }
    376       focused_hwnd = GetParent(focused_hwnd);
    377     }
    378 
    379     if (regain_next_lost_focus_) {
    380       regain_next_lost_focus_ = false;
    381       view_->GetWidget()->Activate();
    382       return;
    383     }
    384 
    385     // If we get here, the focused window is not the taskbar, it's context menu,
    386     // or the app list.
    387     on_active_lost_.Run();
    388   }
    389 
    390  private:
    391   // The window to track the active state of.
    392   app_list::AppListView* view_;
    393 
    394   // Called to request |view_| be closed.
    395   base::Closure on_active_lost_;
    396 
    397   // True if we are anticipating that the app list will lose focus, and we want
    398   // to take it back. This is used when switching out of Metro mode, and the
    399   // browser regains focus after showing the app list.
    400   bool regain_next_lost_focus_;
    401 
    402   // When the context menu on the app list's taskbar icon is brought up the
    403   // app list should not be hidden, but it should be if the taskbar is clicked
    404   // on. There can be a period of time when the taskbar gets focus between a
    405   // right mouse click and the menu showing; to prevent hiding the app launcher
    406   // when this happens it is kept visible if the taskbar is seen briefly without
    407   // the right mouse button down, but not if this happens twice in a row.
    408   bool preserving_focus_for_taskbar_menu_;
    409 
    410   // Timer used to check if the taskbar or app list is active. Using a timer
    411   // means we don't need to hook Windows, which is apparently not possible
    412   // since Vista (and is not nice at any time).
    413   base::RepeatingTimer<ActivationTracker> timer_;
    414 };
    415 
    416 // The AppListController class manages global resources needed for the app
    417 // list to operate, and controls when the app list is opened and closed.
    418 // TODO(tapted): Rename this class to AppListServiceWin and move entire file to
    419 // chrome/browser/ui/app_list/app_list_service_win.cc after removing
    420 // chrome/browser/ui/views dependency.
    421 class AppListController : public AppListServiceImpl {
    422  public:
    423   virtual ~AppListController();
    424 
    425   static AppListController* GetInstance() {
    426     return Singleton<AppListController,
    427                      LeakySingletonTraits<AppListController> >::get();
    428   }
    429 
    430   void set_can_close(bool can_close) { can_close_app_list_ = can_close; }
    431   bool can_close() { return can_close_app_list_; }
    432 
    433   void AppListClosing();
    434   void AppListActivationChanged(bool active);
    435   void ShowAppListDuringModeSwitch(Profile* requested_profile);
    436 
    437   app_list::AppListView* GetView() { return current_view_; }
    438 
    439   // AppListService overrides:
    440   virtual void HandleFirstRun() OVERRIDE;
    441   virtual void Init(Profile* initial_profile) OVERRIDE;
    442   virtual void CreateForProfile(Profile* requested_profile) OVERRIDE;
    443   virtual void ShowForProfile(Profile* requested_profile) OVERRIDE;
    444   virtual void DismissAppList() OVERRIDE;
    445   virtual bool IsAppListVisible() const OVERRIDE;
    446   virtual gfx::NativeWindow GetAppListWindow() OVERRIDE;
    447   virtual AppListControllerDelegate* CreateControllerDelegate() OVERRIDE;
    448 
    449   // AppListServiceImpl overrides:
    450   virtual void CreateShortcut() OVERRIDE;
    451   virtual void OnSigninStatusChanged() OVERRIDE;
    452 
    453  private:
    454   friend struct DefaultSingletonTraits<AppListController>;
    455 
    456   AppListController();
    457 
    458   bool IsWarmupNeeded();
    459   void ScheduleWarmup();
    460 
    461   // Loads the profile last used with the app list and populates the view from
    462   // it without showing it so that the next show is faster. Does nothing if the
    463   // view already exists, or another profile is in the middle of being loaded to
    464   // be shown.
    465   void LoadProfileForWarmup();
    466   void OnLoadProfileForWarmup(Profile* initial_profile);
    467 
    468   // Creates an AppListView.
    469   app_list::AppListView* CreateAppListView();
    470 
    471   // Customizes the app list |hwnd| for Windows (eg: disable aero peek, set up
    472   // restart params).
    473   void SetWindowAttributes(HWND hwnd);
    474 
    475   // Utility methods for showing the app list.
    476   gfx::Point FindAnchorPoint(const gfx::Display& display,
    477                              const gfx::Point& cursor);
    478   void UpdateArrowPositionAndAnchorPoint(const gfx::Point& cursor);
    479   string16 GetAppListIconPath();
    480 
    481   // Check if the app list or the taskbar has focus. The app list is kept
    482   // visible whenever either of these have focus, which allows it to be
    483   // pinned but will hide it if it otherwise loses focus. This is checked
    484   // periodically whenever the app list does not have focus.
    485   void CheckTaskbarOrViewHasFocus();
    486 
    487   // Utilities to manage browser process keep alive for the view itself. Note
    488   // keep alives are also used when asynchronously loading profiles.
    489   void EnsureHaveKeepAliveForView();
    490   void FreeAnyKeepAliveForView();
    491 
    492   // Weak pointer. The view manages its own lifetime.
    493   app_list::AppListView* current_view_;
    494 
    495   scoped_ptr<ActivationTracker> activation_tracker_;
    496 
    497   app_list::PaginationModel pagination_model_;
    498 
    499   // True if the controller can close the app list.
    500   bool can_close_app_list_;
    501 
    502   // Used to keep the browser process alive while the app list is visible.
    503   scoped_ptr<ScopedKeepAlive> keep_alive_;
    504 
    505   bool enable_app_list_on_next_init_;
    506 
    507   base::WeakPtrFactory<AppListController> weak_factory_;
    508 
    509   DISALLOW_COPY_AND_ASSIGN(AppListController);
    510 };
    511 
    512 AppListControllerDelegateWin::AppListControllerDelegateWin() {}
    513 
    514 AppListControllerDelegateWin::~AppListControllerDelegateWin() {}
    515 
    516 void AppListControllerDelegateWin::DismissView() {
    517   AppListController::GetInstance()->DismissAppList();
    518 }
    519 
    520 void AppListControllerDelegateWin::ViewActivationChanged(bool active) {
    521   AppListController::GetInstance()->AppListActivationChanged(active);
    522 }
    523 
    524 void AppListControllerDelegateWin::ViewClosing() {
    525   AppListController::GetInstance()->AppListClosing();
    526 }
    527 
    528 gfx::NativeWindow AppListControllerDelegateWin::GetAppListWindow() {
    529   return AppListController::GetInstance()->GetAppListWindow();
    530 }
    531 
    532 gfx::ImageSkia AppListControllerDelegateWin::GetWindowIcon() {
    533   gfx::ImageSkia* resource = ResourceBundle::GetSharedInstance().
    534       GetImageSkiaNamed(chrome::GetAppListIconResourceId());
    535   return *resource;
    536 }
    537 
    538 bool AppListControllerDelegateWin::CanPin() {
    539   return false;
    540 }
    541 
    542 void AppListControllerDelegateWin::OnShowExtensionPrompt() {
    543   AppListController::GetInstance()->set_can_close(false);
    544 }
    545 
    546 void AppListControllerDelegateWin::OnCloseExtensionPrompt() {
    547   AppListController::GetInstance()->set_can_close(true);
    548 }
    549 
    550 bool AppListControllerDelegateWin::CanDoCreateShortcutsFlow(
    551     bool is_platform_app) {
    552   return true;
    553 }
    554 
    555 void AppListControllerDelegateWin::DoCreateShortcutsFlow(
    556     Profile* profile,
    557     const std::string& extension_id) {
    558   ExtensionService* service =
    559       extensions::ExtensionSystem::Get(profile)->extension_service();
    560   DCHECK(service);
    561   const extensions::Extension* extension = service->GetInstalledExtension(
    562       extension_id);
    563   DCHECK(extension);
    564 
    565   app_list::AppListView* view = AppListController::GetInstance()->GetView();
    566   if (!view)
    567     return;
    568 
    569   gfx::NativeWindow parent_hwnd =
    570       view->GetWidget()->GetTopLevelWidget()->GetNativeWindow();
    571   OnShowExtensionPrompt();
    572   chrome::ShowCreateChromeAppShortcutsDialog(
    573       parent_hwnd, profile, extension,
    574       base::Bind(&AppListControllerDelegateWin::OnCloseExtensionPrompt,
    575                  base::Unretained(this)));
    576 }
    577 
    578 void AppListControllerDelegateWin::CreateNewWindow(Profile* profile,
    579                                                    bool incognito) {
    580   Profile* window_profile = incognito ?
    581       profile->GetOffTheRecordProfile() : profile;
    582   chrome::NewEmptyWindow(window_profile, chrome::GetActiveDesktop());
    583 }
    584 
    585 void AppListControllerDelegateWin::ActivateApp(
    586     Profile* profile, const extensions::Extension* extension, int event_flags) {
    587   LaunchApp(profile, extension, event_flags);
    588 }
    589 
    590 void AppListControllerDelegateWin::LaunchApp(
    591     Profile* profile, const extensions::Extension* extension, int event_flags) {
    592   AppListServiceImpl::RecordAppListAppLaunch();
    593   chrome::OpenApplication(chrome::AppLaunchParams(
    594       profile, extension, NEW_FOREGROUND_TAB));
    595 }
    596 
    597 AppListController::AppListController()
    598     : current_view_(NULL),
    599       can_close_app_list_(true),
    600       enable_app_list_on_next_init_(false),
    601       weak_factory_(this) {}
    602 
    603 AppListController::~AppListController() {
    604 }
    605 
    606 gfx::NativeWindow AppListController::GetAppListWindow() {
    607   if (!IsAppListVisible())
    608     return NULL;
    609   return current_view_ ? current_view_->GetWidget()->GetNativeWindow() : NULL;
    610 }
    611 
    612 AppListControllerDelegate* AppListController::CreateControllerDelegate() {
    613   return new AppListControllerDelegateWin();
    614 }
    615 
    616 void AppListController::OnSigninStatusChanged() {
    617   if (current_view_)
    618     current_view_->OnSigninStatusChanged();
    619 }
    620 
    621 void AppListController::ShowForProfile(Profile* requested_profile) {
    622   DCHECK(requested_profile);
    623   if (requested_profile->IsManaged())
    624     return;
    625 
    626   ScopedKeepAlive show_app_list_keepalive;
    627 
    628   content::BrowserThread::PostBlockingPoolTask(
    629       FROM_HERE, base::Bind(SetDidRunForNDayActiveStats));
    630 
    631   if (win8::IsSingleWindowMetroMode()) {
    632     // This request came from Windows 8 in desktop mode, but chrome is currently
    633     // running in Metro mode.
    634     AppMetroInfoBarDelegateWin::Create(
    635         requested_profile, AppMetroInfoBarDelegateWin::SHOW_APP_LIST,
    636         std::string());
    637     return;
    638   }
    639 
    640   InvalidatePendingProfileLoads();
    641 
    642   // If the app list is already displaying |profile| just activate it (in case
    643   // we have lost focus).
    644   if (IsAppListVisible() && (requested_profile == profile())) {
    645     current_view_->GetWidget()->Show();
    646     current_view_->GetWidget()->Activate();
    647     return;
    648   }
    649 
    650   SetProfilePath(requested_profile->GetPath());
    651 
    652   DismissAppList();
    653   CreateForProfile(requested_profile);
    654 
    655   DCHECK(current_view_);
    656   EnsureHaveKeepAliveForView();
    657   gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
    658   UpdateArrowPositionAndAnchorPoint(cursor);
    659   current_view_->GetWidget()->Show();
    660   current_view_->GetWidget()->GetTopLevelWidget()->UpdateWindowIcon();
    661   current_view_->GetWidget()->Activate();
    662   RecordAppListLaunch();
    663 }
    664 
    665 void AppListController::ShowAppListDuringModeSwitch(
    666     Profile* requested_profile) {
    667   ShowForProfile(requested_profile);
    668   activation_tracker_->RegainNextLostFocus();
    669 }
    670 
    671 void AppListController::CreateForProfile(Profile* requested_profile) {
    672   // Aura has problems with layered windows and bubble delegates. The app
    673   // launcher has a trick where it only hides the window when it is dismissed,
    674   // reshowing it again later. This does not work with win aura for some
    675   // reason. This change temporarily makes it always get recreated, only on win
    676   // aura. See http://crbug.com/176186.
    677 #if !defined(USE_AURA)
    678   if (requested_profile == profile())
    679     return;
    680 #endif
    681 
    682   SetProfile(requested_profile);
    683   current_view_ = CreateAppListView();
    684   activation_tracker_.reset(new ActivationTracker(current_view_,
    685       base::Bind(&AppListController::DismissAppList, base::Unretained(this))));
    686 }
    687 
    688 app_list::AppListView* AppListController::CreateAppListView() {
    689   // The controller will be owned by the view delegate, and the delegate is
    690   // owned by the app list view. The app list view manages it's own lifetime.
    691   AppListViewDelegate* view_delegate =
    692       new AppListViewDelegate(CreateControllerDelegate(), profile());
    693   app_list::AppListView* view = new app_list::AppListView(view_delegate);
    694   gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
    695   view->InitAsBubble(NULL,
    696                      &pagination_model_,
    697                      NULL,
    698                      cursor,
    699                      views::BubbleBorder::FLOAT,
    700                      false /* border_accepts_events */);
    701   SetWindowAttributes(view->GetHWND());
    702   return view;
    703 }
    704 
    705 void AppListController::SetWindowAttributes(HWND hwnd) {
    706   // Vista and lower do not offer pinning to the taskbar, which makes any
    707   // presence on the taskbar useless. So, hide the window on the taskbar
    708   // for these versions of Windows.
    709   if (base::win::GetVersion() <= base::win::VERSION_VISTA) {
    710     LONG_PTR ex_styles = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
    711     ex_styles |= WS_EX_TOOLWINDOW;
    712     SetWindowLongPtr(hwnd, GWL_EXSTYLE, ex_styles);
    713   }
    714 
    715   if (base::win::GetVersion() > base::win::VERSION_VISTA) {
    716     // Disable aero peek. Without this, hovering over the taskbar popup puts
    717     // Windows into a mode for switching between windows in the same
    718     // application. The app list has just one window, so it is just distracting.
    719     BOOL disable_value = TRUE;
    720     ::DwmSetWindowAttribute(hwnd,
    721                             DWMWA_DISALLOW_PEEK,
    722                             &disable_value,
    723                             sizeof(disable_value));
    724   }
    725 
    726   ui::win::SetAppIdForWindow(GetAppModelId(), hwnd);
    727   CommandLine relaunch = GetAppListCommandLine();
    728   string16 app_name(GetAppListShortcutName());
    729   ui::win::SetRelaunchDetailsForWindow(
    730       relaunch.GetCommandLineString(), app_name, hwnd);
    731   ::SetWindowText(hwnd, app_name.c_str());
    732   string16 icon_path = GetAppListIconPath();
    733   ui::win::SetAppIconForWindow(icon_path, hwnd);
    734 }
    735 
    736 void AppListController::DismissAppList() {
    737   if (IsAppListVisible() && can_close_app_list_) {
    738     current_view_->GetWidget()->Hide();
    739     activation_tracker_->OnViewHidden();
    740     FreeAnyKeepAliveForView();
    741   }
    742 }
    743 
    744 void AppListController::AppListClosing() {
    745   FreeAnyKeepAliveForView();
    746   current_view_ = NULL;
    747   activation_tracker_.reset();
    748   SetProfile(NULL);
    749 }
    750 
    751 void AppListController::AppListActivationChanged(bool active) {
    752   activation_tracker_->OnActivationChanged(active);
    753 }
    754 
    755 // Attempts to find the bounds of the Windows taskbar. Returns true on success.
    756 // |rect| is in screen coordinates. If the taskbar is in autohide mode and is
    757 // not visible, |rect| will be outside the current monitor's bounds, except for
    758 // one pixel of overlap where the edge of the taskbar is shown.
    759 bool GetTaskbarRect(gfx::Rect* rect) {
    760   HWND taskbar_hwnd = FindWindow(kTrayClassName, NULL);
    761   if (!taskbar_hwnd)
    762     return false;
    763 
    764   RECT win_rect;
    765   if (!GetWindowRect(taskbar_hwnd, &win_rect))
    766     return false;
    767 
    768   *rect = gfx::Rect(win_rect);
    769   return true;
    770 }
    771 
    772 gfx::Point FindReferencePoint(const gfx::Display& display,
    773                               const gfx::Point& cursor) {
    774   const int kSnapDistance = 50;
    775 
    776   // If we can't find the taskbar, snap to the bottom left.
    777   // If the display size is the same as the work area, and does not contain the
    778   // taskbar, either the taskbar is hidden or on another monitor, so just snap
    779   // to the bottom left.
    780   gfx::Rect taskbar_rect;
    781   if (!GetTaskbarRect(&taskbar_rect) ||
    782       (display.work_area() == display.bounds() &&
    783           !display.work_area().Contains(taskbar_rect))) {
    784     return display.work_area().bottom_left();
    785   }
    786 
    787   // Snap to the taskbar edge. If the cursor is greater than kSnapDistance away,
    788   // also move to the left (for horizontal taskbars) or top (for vertical).
    789   const gfx::Rect& screen_rect = display.bounds();
    790   // First handle taskbar on bottom.
    791   // Note on Windows 8 the work area won't include split windows on the left or
    792   // right, and neither will |taskbar_rect|.
    793   if (taskbar_rect.width() == display.work_area().width()) {
    794     if (taskbar_rect.bottom() == screen_rect.bottom()) {
    795       if (taskbar_rect.y() - cursor.y() > kSnapDistance)
    796         return gfx::Point(screen_rect.x(), taskbar_rect.y());
    797 
    798       return gfx::Point(cursor.x(), taskbar_rect.y());
    799     }
    800 
    801     // Now try on the top.
    802     if (cursor.y() - taskbar_rect.bottom() > kSnapDistance)
    803       return gfx::Point(screen_rect.x(), taskbar_rect.bottom());
    804 
    805     return gfx::Point(cursor.x(), taskbar_rect.bottom());
    806   }
    807 
    808   // Now try the left.
    809   if (taskbar_rect.x() == screen_rect.x()) {
    810     if (cursor.x() - taskbar_rect.right() > kSnapDistance)
    811       return gfx::Point(taskbar_rect.right(), screen_rect.y());
    812 
    813     return gfx::Point(taskbar_rect.right(), cursor.y());
    814   }
    815 
    816   // Finally, try the right.
    817   if (taskbar_rect.x() - cursor.x() > kSnapDistance)
    818     return gfx::Point(taskbar_rect.x(), screen_rect.y());
    819 
    820   return gfx::Point(taskbar_rect.x(), cursor.y());
    821 }
    822 
    823 gfx::Point AppListController::FindAnchorPoint(
    824     const gfx::Display& display,
    825     const gfx::Point& cursor) {
    826   const int kSnapOffset = 3;
    827 
    828   gfx::Rect bounds_rect(display.work_area());
    829   // Always subtract the taskbar area since work_area() will not subtract it if
    830   // the taskbar is set to auto-hide, and the app list should never overlap the
    831   // taskbar.
    832   gfx::Rect taskbar_rect;
    833   if (GetTaskbarRect(&taskbar_rect))
    834     bounds_rect.Subtract(taskbar_rect);
    835 
    836   gfx::Size view_size(current_view_->GetPreferredSize());
    837   bounds_rect.Inset(view_size.width() / 2 + kSnapOffset,
    838                     view_size.height() / 2 + kSnapOffset);
    839 
    840   gfx::Point anchor = FindReferencePoint(display, cursor);
    841   anchor.SetToMax(bounds_rect.origin());
    842   anchor.SetToMin(bounds_rect.bottom_right());
    843   return anchor;
    844 }
    845 
    846 void AppListController::UpdateArrowPositionAndAnchorPoint(
    847     const gfx::Point& cursor) {
    848   gfx::Screen* screen =
    849       gfx::Screen::GetScreenFor(current_view_->GetWidget()->GetNativeView());
    850   gfx::Display display = screen->GetDisplayNearestPoint(cursor);
    851 
    852   current_view_->SetBubbleArrow(views::BubbleBorder::FLOAT);
    853   current_view_->SetAnchorPoint(FindAnchorPoint(display, cursor));
    854 }
    855 
    856 string16 AppListController::GetAppListIconPath() {
    857   base::FilePath icon_path;
    858   if (!PathService::Get(base::FILE_EXE, &icon_path)) {
    859     NOTREACHED();
    860     return string16();
    861   }
    862 
    863   std::stringstream ss;
    864   ss << "," << GetAppListIconIndex();
    865   string16 result = icon_path.value();
    866   result.append(UTF8ToUTF16(ss.str()));
    867   return result;
    868 }
    869 
    870 void AppListController::EnsureHaveKeepAliveForView() {
    871   if (!keep_alive_)
    872     keep_alive_.reset(new ScopedKeepAlive());
    873 }
    874 
    875 void AppListController::FreeAnyKeepAliveForView() {
    876   if (keep_alive_)
    877     keep_alive_.reset(NULL);
    878 }
    879 
    880 void AppListController::OnLoadProfileForWarmup(Profile* initial_profile) {
    881   if (!IsWarmupNeeded())
    882     return;
    883 
    884   CreateForProfile(initial_profile);
    885   current_view_->Prerender();
    886 }
    887 
    888 void AppListController::HandleFirstRun() {
    889   PrefService* local_state = g_browser_process->local_state();
    890   // If the app list is already enabled during first run, then the user had
    891   // opted in to the app launcher before uninstalling, so we re-enable to
    892   // restore shortcuts to the app list.
    893   // Note we can't directly create the shortcuts here because the IO thread
    894   // hasn't been created yet.
    895   enable_app_list_on_next_init_ = local_state->GetBoolean(
    896       apps::prefs::kAppLauncherHasBeenEnabled);
    897 }
    898 
    899 void AppListController::Init(Profile* initial_profile) {
    900   // In non-Ash metro mode, we can not show the app list for this process, so do
    901   // not bother performing Init tasks.
    902   if (win8::IsSingleWindowMetroMode())
    903     return;
    904 
    905   if (enable_app_list_on_next_init_) {
    906     enable_app_list_on_next_init_ = false;
    907     EnableAppList(initial_profile);
    908     CreateShortcut();
    909   }
    910 
    911   PrefService* prefs = g_browser_process->local_state();
    912   if (prefs->HasPrefPath(prefs::kRestartWithAppList) &&
    913       prefs->GetBoolean(prefs::kRestartWithAppList)) {
    914     prefs->SetBoolean(prefs::kRestartWithAppList, false);
    915     AppListController::GetInstance()->
    916         ShowAppListDuringModeSwitch(initial_profile);
    917   }
    918 
    919   // Migrate from legacy app launcher if we are on a non-canary and non-chromium
    920   // build.
    921 #if defined(GOOGLE_CHROME_BUILD)
    922   if (!InstallUtil::IsChromeSxSProcess() &&
    923       !chrome_launcher_support::GetAnyAppHostPath().empty()) {
    924     chrome_launcher_support::InstallationState state =
    925         chrome_launcher_support::GetAppLauncherInstallationState();
    926     if (state == chrome_launcher_support::NOT_INSTALLED) {
    927       // If app_host.exe is found but can't be located in the registry,
    928       // skip the migration as this is likely a developer build.
    929       return;
    930     } else if (state == chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL) {
    931       chrome_launcher_support::UninstallLegacyAppLauncher(
    932           chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION);
    933     } else if (state == chrome_launcher_support::INSTALLED_AT_USER_LEVEL) {
    934       chrome_launcher_support::UninstallLegacyAppLauncher(
    935           chrome_launcher_support::USER_LEVEL_INSTALLATION);
    936     }
    937     EnableAppList(initial_profile);
    938     CreateShortcut();
    939   }
    940 #endif
    941 
    942   // Instantiate AppListController so it listens for profile deletions.
    943   AppListController::GetInstance();
    944 
    945   ScheduleWarmup();
    946 
    947   MigrateAppLauncherEnabledPref();
    948   HandleCommandLineFlags(initial_profile);
    949 }
    950 
    951 bool AppListController::IsAppListVisible() const {
    952   return current_view_ && current_view_->GetWidget()->IsVisible();
    953 }
    954 
    955 void AppListController::CreateShortcut() {
    956   // Check if the app launcher shortcuts have ever been created before.
    957   // Shortcuts should only be created once. If the user unpins the taskbar
    958   // shortcut, they can restore it by pinning the start menu or desktop
    959   // shortcut.
    960   ShellIntegration::ShortcutLocations shortcut_locations;
    961   shortcut_locations.on_desktop = true;
    962   shortcut_locations.in_quick_launch_bar = true;
    963   shortcut_locations.in_applications_menu = true;
    964   BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    965   shortcut_locations.applications_menu_subdir = dist->GetAppShortCutName();
    966   base::FilePath user_data_dir(
    967       g_browser_process->profile_manager()->user_data_dir());
    968 
    969   content::BrowserThread::PostTask(
    970       content::BrowserThread::FILE,
    971       FROM_HERE,
    972       base::Bind(&CreateAppListShortcuts,
    973                   user_data_dir, GetAppModelId(), shortcut_locations));
    974 }
    975 
    976 void AppListController::ScheduleWarmup() {
    977   // Post a task to create the app list. This is posted to not impact startup
    978   // time.
    979   const int kInitWindowDelay = 5;
    980   base::MessageLoop::current()->PostDelayedTask(
    981       FROM_HERE,
    982       base::Bind(&AppListController::LoadProfileForWarmup,
    983                  weak_factory_.GetWeakPtr()),
    984       base::TimeDelta::FromSeconds(kInitWindowDelay));
    985 
    986   // Send app list usage stats after a delay.
    987   const int kSendUsageStatsDelay = 5;
    988   base::MessageLoop::current()->PostDelayedTask(
    989       FROM_HERE,
    990       base::Bind(&AppListController::SendAppListStats),
    991       base::TimeDelta::FromSeconds(kSendUsageStatsDelay));
    992 }
    993 
    994 bool AppListController::IsWarmupNeeded() {
    995   if (!g_browser_process || g_browser_process->IsShuttingDown())
    996     return false;
    997 
    998   // We only need to initialize the view if there's no view already created and
    999   // there's no profile loading to be shown.
   1000   return !current_view_ && !profile_loader().IsAnyProfileLoading();
   1001 }
   1002 
   1003 void AppListController::LoadProfileForWarmup() {
   1004   if (!IsWarmupNeeded())
   1005     return;
   1006 
   1007   ProfileManager* profile_manager = g_browser_process->profile_manager();
   1008   base::FilePath profile_path(GetProfilePath(profile_manager->user_data_dir()));
   1009 
   1010   profile_loader().LoadProfileInvalidatingOtherLoads(
   1011       profile_path,
   1012       base::Bind(&AppListController::OnLoadProfileForWarmup,
   1013                  weak_factory_.GetWeakPtr()));
   1014 }
   1015 
   1016 }  // namespace
   1017 
   1018 namespace chrome {
   1019 
   1020 AppListService* GetAppListServiceWin() {
   1021   return AppListController::GetInstance();
   1022 }
   1023 
   1024 }  // namespace chrome
   1025