root/chrome/browser/ui/views/app_list/win/app_list_service_win.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Get
  2. InitAll
  3. MigrateAppLauncherEnabledPref
  4. GetAppListIconIndex
  5. GetAppListIconPath
  6. GetAppListShortcutName
  7. GetAppListCommandLine
  8. GetAppModelId
  9. SetDidRunForNDayActiveStats
  10. CreateAppListShortcuts
  11. SetWindowAttributes
  12. CreateAppList
  13. GetInstance
  14. weak_factory_
  15. set_can_close
  16. GetAppListWindow
  17. GetCurrentAppListProfile
  18. GetControllerDelegate
  19. ShowForProfile
  20. DismissAppList
  21. OnAppListClosing
  22. OnLoadProfileForWarmup
  23. SetAppListNextPaintCallback
  24. HandleFirstRun
  25. Init
  26. CreateForProfile
  27. IsAppListVisible
  28. CreateShortcut
  29. ScheduleWarmup
  30. IsWarmupNeeded
  31. LoadProfileForWarmup
  32. GetAppListServiceWin

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/app_list/win/app_list_service_win.h"

#include <dwmapi.h>
#include <sstream>

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/win/shortcut.h"
#include "base/win/windows_version.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/app_list/app_list.h"
#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
#include "chrome/browser/ui/app_list/app_list_factory.h"
#include "chrome/browser/ui/app_list/app_list_service_impl.h"
#include "chrome/browser/ui/app_list/app_list_shower.h"
#include "chrome/browser/ui/app_list/app_list_view_delegate.h"
#include "chrome/browser/ui/app_list/keep_alive_service_impl.h"
#include "chrome/browser/ui/apps/app_metro_infobar_delegate_win.h"
#include "chrome/browser/ui/views/app_list/win/activation_tracker_win.h"
#include "chrome/browser/ui/views/app_list/win/app_list_controller_delegate_win.h"
#include "chrome/browser/ui/views/app_list/win/app_list_win.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/launcher_support/chrome_launcher_support.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/util_constants.h"
#include "content/public/browser/browser_thread.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/google_chrome_strings.h"
#include "grit/theme_resources.h"
#include "ui/app_list/app_list_model.h"
#include "ui/app_list/pagination_model.h"
#include "ui/app_list/views/app_list_view.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/win/shell.h"
#include "ui/gfx/display.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/screen.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/widget/widget.h"
#include "win8/util/win8_util.h"

#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#endif

#if defined(USE_ASH)
#include "chrome/browser/ui/app_list/app_list_service_ash.h"
#include "chrome/browser/ui/host_desktop.h"
#endif

#if defined(GOOGLE_CHROME_BUILD)
#include "chrome/installer/util/install_util.h"
#endif

// static
AppListService* AppListService::Get(chrome::HostDesktopType desktop_type) {
#if defined(USE_ASH)
  if (desktop_type == chrome::HOST_DESKTOP_TYPE_ASH)
    return chrome::GetAppListServiceAsh();
#endif

  return chrome::GetAppListServiceWin();
}

// static
void AppListService::InitAll(Profile* initial_profile) {
#if defined(USE_ASH)
  chrome::GetAppListServiceAsh()->Init(initial_profile);
#endif
  chrome::GetAppListServiceWin()->Init(initial_profile);
}

namespace {

// Migrate chrome::kAppLauncherIsEnabled pref to
// chrome::kAppLauncherHasBeenEnabled pref.
void MigrateAppLauncherEnabledPref() {
  PrefService* prefs = g_browser_process->local_state();
  if (prefs->HasPrefPath(prefs::kAppLauncherIsEnabled)) {
    prefs->SetBoolean(prefs::kAppLauncherHasBeenEnabled,
                      prefs->GetBoolean(prefs::kAppLauncherIsEnabled));
    prefs->ClearPref(prefs::kAppLauncherIsEnabled);
  }
}

int GetAppListIconIndex() {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  return dist->GetIconIndex(BrowserDistribution::SHORTCUT_APP_LAUNCHER);
}

base::string16 GetAppListIconPath() {
  base::FilePath icon_path;
  if (!PathService::Get(base::FILE_EXE, &icon_path)) {
    NOTREACHED();
    return base::string16();
  }

  std::stringstream ss;
  ss << "," << GetAppListIconIndex();
  base::string16 result = icon_path.value();
  result.append(base::UTF8ToUTF16(ss.str()));
  return result;
}

base::string16 GetAppListShortcutName() {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  return dist->GetShortcutName(BrowserDistribution::SHORTCUT_APP_LAUNCHER);
}

CommandLine GetAppListCommandLine() {
  const char* const kSwitchesToCopy[] = { switches::kUserDataDir };
  CommandLine* current = CommandLine::ForCurrentProcess();
  base::FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
     NOTREACHED();
     return CommandLine(CommandLine::NO_PROGRAM);
  }
  CommandLine command_line(chrome_exe);
  command_line.CopySwitchesFrom(*current, kSwitchesToCopy,
                                arraysize(kSwitchesToCopy));
  command_line.AppendSwitch(switches::kShowAppList);
  return command_line;
}

base::string16 GetAppModelId() {
  // The AppModelId should be the same for all profiles in a user data directory
  // but different for different user data directories, so base it on the
  // initial profile in the current user data directory.
  base::FilePath initial_profile_path;
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kUserDataDir)) {
    initial_profile_path =
        command_line->GetSwitchValuePath(switches::kUserDataDir).AppendASCII(
            chrome::kInitialProfile);
  }
  return ShellIntegration::GetAppListAppModelIdForProfile(initial_profile_path);
}

void SetDidRunForNDayActiveStats() {
  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
  base::FilePath exe_path;
  if (!PathService::Get(base::DIR_EXE, &exe_path)) {
    NOTREACHED();
    return;
  }
  bool system_install =
      !InstallUtil::IsPerUserInstall(exe_path.value().c_str());
  // Using Chrome Binary dist: Chrome dist may not exist for the legacy
  // App Launcher, and App Launcher dist may be "shadow", which does not
  // contain the information needed to determine multi-install.
  // Edge case involving Canary: crbug/239163.
  BrowserDistribution* chrome_binaries_dist =
      BrowserDistribution::GetSpecificDistribution(
          BrowserDistribution::CHROME_BINARIES);
  if (chrome_binaries_dist &&
      InstallUtil::IsMultiInstall(chrome_binaries_dist, system_install)) {
    BrowserDistribution* app_launcher_dist =
        BrowserDistribution::GetSpecificDistribution(
            BrowserDistribution::CHROME_APP_HOST);
    GoogleUpdateSettings::UpdateDidRunStateForDistribution(
        app_launcher_dist,
        true /* did_run */,
        system_install);
  }
}

// The start menu shortcut is created on first run by users that are
// upgrading. The desktop and taskbar shortcuts are created the first time the
// user enables the app list. The taskbar shortcut is created in
// |user_data_dir| and will use a Windows Application Model Id of
// |app_model_id|. This runs on the FILE thread and not in the blocking IO
// thread pool as there are other tasks running (also on the FILE thread)
// which fiddle with shortcut icons
// (ShellIntegration::MigrateWin7ShortcutsOnPath). Having different threads
// fiddle with the same shortcuts could cause race issues.
void CreateAppListShortcuts(
    const base::FilePath& user_data_dir,
    const base::string16& app_model_id,
    const ShellIntegration::ShortcutLocations& creation_locations) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);

  // Shortcut paths under which to create shortcuts.
  std::vector<base::FilePath> shortcut_paths =
      web_app::internals::GetShortcutPaths(creation_locations);

  bool pin_to_taskbar = creation_locations.in_quick_launch_bar &&
                        (base::win::GetVersion() >= base::win::VERSION_WIN7);

  // Create a shortcut in the |user_data_dir| for taskbar pinning.
  if (pin_to_taskbar)
    shortcut_paths.push_back(user_data_dir);
  bool success = true;

  base::FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
    NOTREACHED();
    return;
  }

  base::string16 app_list_shortcut_name = GetAppListShortcutName();

  base::string16 wide_switches(GetAppListCommandLine().GetArgumentsString());

  base::win::ShortcutProperties shortcut_properties;
  shortcut_properties.set_target(chrome_exe);
  shortcut_properties.set_working_dir(chrome_exe.DirName());
  shortcut_properties.set_arguments(wide_switches);
  shortcut_properties.set_description(app_list_shortcut_name);
  shortcut_properties.set_icon(chrome_exe, GetAppListIconIndex());
  shortcut_properties.set_app_id(app_model_id);

  for (size_t i = 0; i < shortcut_paths.size(); ++i) {
    base::FilePath shortcut_file =
        shortcut_paths[i].Append(app_list_shortcut_name).
            AddExtension(installer::kLnkExt);
    if (!base::PathExists(shortcut_file.DirName()) &&
        !base::CreateDirectory(shortcut_file.DirName())) {
      NOTREACHED();
      return;
    }
    success = success && base::win::CreateOrUpdateShortcutLink(
        shortcut_file, shortcut_properties,
        base::win::SHORTCUT_CREATE_ALWAYS);
  }

  if (success && pin_to_taskbar) {
    base::FilePath shortcut_to_pin =
        user_data_dir.Append(app_list_shortcut_name).
            AddExtension(installer::kLnkExt);
    success = base::win::TaskbarPinShortcutLink(
        shortcut_to_pin.value().c_str()) && success;
  }
}

// Customizes the app list |hwnd| for Windows (eg: disable aero peek, set up
// restart params).
void SetWindowAttributes(HWND hwnd) {
  if (base::win::GetVersion() > base::win::VERSION_VISTA) {
    // Disable aero peek. Without this, hovering over the taskbar popup puts
    // Windows into a mode for switching between windows in the same
    // application. The app list has just one window, so it is just distracting.
    BOOL disable_value = TRUE;
    ::DwmSetWindowAttribute(hwnd,
                            DWMWA_DISALLOW_PEEK,
                            &disable_value,
                            sizeof(disable_value));
  }

  ui::win::SetAppIdForWindow(GetAppModelId(), hwnd);
  CommandLine relaunch = GetAppListCommandLine();
  base::string16 app_name(GetAppListShortcutName());
  ui::win::SetRelaunchDetailsForWindow(
      relaunch.GetCommandLineString(), app_name, hwnd);
  ::SetWindowText(hwnd, app_name.c_str());
  base::string16 icon_path = GetAppListIconPath();
  ui::win::SetAppIconForWindow(icon_path, hwnd);
}

class AppListFactoryWin : public AppListFactory {
 public:
  explicit AppListFactoryWin(AppListServiceWin* service)
      : service_(service) {
  }

  virtual ~AppListFactoryWin() {
  }

  virtual AppList* CreateAppList(
      Profile* profile,
      AppListService* service,
      const base::Closure& on_should_dismiss) OVERRIDE {
    // The view delegate will be owned by the app list view. The app list view
    // manages it's own lifetime.
    AppListViewDelegate* view_delegate =
        new AppListViewDelegate(profile,
                                service->GetControllerDelegate());
    app_list::AppListView* view = new app_list::AppListView(view_delegate);
    gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
    view->InitAsBubbleAtFixedLocation(NULL,
                                      &pagination_model_,
                                      cursor,
                                      views::BubbleBorder::FLOAT,
                                      false /* border_accepts_events */);
    SetWindowAttributes(view->GetHWND());
    return new AppListWin(view, on_should_dismiss);
  }

 private:
  // PaginationModel that is shared across all views.
  app_list::PaginationModel pagination_model_;
  AppListServiceWin* service_;

  DISALLOW_COPY_AND_ASSIGN(AppListFactoryWin);
};

}  // namespace

// static
AppListServiceWin* AppListServiceWin::GetInstance() {
  return Singleton<AppListServiceWin,
                   LeakySingletonTraits<AppListServiceWin> >::get();
}

AppListServiceWin::AppListServiceWin()
    : enable_app_list_on_next_init_(false),
      shower_(new AppListShower(
          scoped_ptr<AppListFactory>(new AppListFactoryWin(this)),
          scoped_ptr<KeepAliveService>(new KeepAliveServiceImpl),
          this)),
      controller_delegate_(new AppListControllerDelegateWin(this)),
      weak_factory_(this) {
}

AppListServiceWin::~AppListServiceWin() {
}

void AppListServiceWin::set_can_close(bool can_close) {
  shower_->set_can_close(can_close);
}

gfx::NativeWindow AppListServiceWin::GetAppListWindow() {
  return shower_->GetWindow();
}

Profile* AppListServiceWin::GetCurrentAppListProfile() {
  return shower_->profile();
}

AppListControllerDelegate* AppListServiceWin::GetControllerDelegate() {
  return controller_delegate_.get();
}

void AppListServiceWin::ShowForProfile(Profile* requested_profile) {
  DCHECK(requested_profile);
  if (requested_profile->IsManaged())
    return;

  ScopedKeepAlive show_app_list_keepalive;

  content::BrowserThread::PostBlockingPoolTask(
      FROM_HERE, base::Bind(SetDidRunForNDayActiveStats));

  if (win8::IsSingleWindowMetroMode()) {
    // This request came from Windows 8 in desktop mode, but chrome is currently
    // running in Metro mode.
    AppMetroInfoBarDelegateWin::Create(
        requested_profile, AppMetroInfoBarDelegateWin::SHOW_APP_LIST,
        std::string());
    return;
  }

  InvalidatePendingProfileLoads();
  SetProfilePath(requested_profile->GetPath());
  shower_->ShowForProfile(requested_profile);
  RecordAppListLaunch();
}

void AppListServiceWin::DismissAppList() {
  shower_->DismissAppList();
}

void AppListServiceWin::OnAppListClosing() {
  shower_->CloseAppList();
}

void AppListServiceWin::OnLoadProfileForWarmup(Profile* initial_profile) {
  if (!IsWarmupNeeded())
    return;

  base::Time before_warmup(base::Time::Now());
  shower_->WarmupForProfile(initial_profile);
  UMA_HISTOGRAM_TIMES("Apps.AppListWarmupDuration",
                      base::Time::Now() - before_warmup);
}

void AppListServiceWin::SetAppListNextPaintCallback(void (*callback)()) {
  app_list::AppListView::SetNextPaintCallback(callback);
}

void AppListServiceWin::HandleFirstRun() {
  PrefService* local_state = g_browser_process->local_state();
  // If the app list is already enabled during first run, then the user had
  // opted in to the app launcher before uninstalling, so we re-enable to
  // restore shortcuts to the app list.
  // Note we can't directly create the shortcuts here because the IO thread
  // hasn't been created yet.
  enable_app_list_on_next_init_ = local_state->GetBoolean(
      prefs::kAppLauncherHasBeenEnabled);
}

void AppListServiceWin::Init(Profile* initial_profile) {
  // In non-Ash metro mode, we can not show the app list for this process, so do
  // not bother performing Init tasks.
  if (win8::IsSingleWindowMetroMode())
    return;

  if (enable_app_list_on_next_init_) {
    enable_app_list_on_next_init_ = false;
    EnableAppList(initial_profile, ENABLE_ON_REINSTALL);
    CreateShortcut();
  }

  PrefService* prefs = g_browser_process->local_state();
  if (prefs->HasPrefPath(prefs::kRestartWithAppList) &&
      prefs->GetBoolean(prefs::kRestartWithAppList)) {
    prefs->SetBoolean(prefs::kRestartWithAppList, false);
    // If we are restarting in Metro mode we will lose focus straight away. We
    // need to reacquire focus when that happens.
    shower_->ShowAndReacquireFocus(initial_profile);
  }

  // Migrate from legacy app launcher if we are on a non-canary and non-chromium
  // build.
#if defined(GOOGLE_CHROME_BUILD)
  if (!InstallUtil::IsChromeSxSProcess() &&
      !chrome_launcher_support::GetAnyAppHostPath().empty()) {
    chrome_launcher_support::InstallationState state =
        chrome_launcher_support::GetAppLauncherInstallationState();
    if (state == chrome_launcher_support::NOT_INSTALLED) {
      // If app_host.exe is found but can't be located in the registry,
      // skip the migration as this is likely a developer build.
      return;
    } else if (state == chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL) {
      chrome_launcher_support::UninstallLegacyAppLauncher(
          chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION);
    } else if (state == chrome_launcher_support::INSTALLED_AT_USER_LEVEL) {
      chrome_launcher_support::UninstallLegacyAppLauncher(
          chrome_launcher_support::USER_LEVEL_INSTALLATION);
    }
    EnableAppList(initial_profile, ENABLE_ON_REINSTALL);
    CreateShortcut();
  }
#endif

  ScheduleWarmup();

  MigrateAppLauncherEnabledPref();
  PerformStartupChecks(initial_profile);
}

void AppListServiceWin::CreateForProfile(Profile* profile) {
  shower_->CreateViewForProfile(profile);
}

bool AppListServiceWin::IsAppListVisible() const {
  return shower_->IsAppListVisible();
}

void AppListServiceWin::CreateShortcut() {
  // Check if the app launcher shortcuts have ever been created before.
  // Shortcuts should only be created once. If the user unpins the taskbar
  // shortcut, they can restore it by pinning the start menu or desktop
  // shortcut.
  ShellIntegration::ShortcutLocations shortcut_locations;
  shortcut_locations.on_desktop = true;
  shortcut_locations.in_quick_launch_bar = true;
  shortcut_locations.applications_menu_location =
      ShellIntegration::APP_MENU_LOCATION_SUBDIR_CHROME;
  base::FilePath user_data_dir(
      g_browser_process->profile_manager()->user_data_dir());

  content::BrowserThread::PostTask(
      content::BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&CreateAppListShortcuts,
                 user_data_dir, GetAppModelId(), shortcut_locations));
}

void AppListServiceWin::ScheduleWarmup() {
  // Post a task to create the app list. This is posted to not impact startup
  // time.
  const int kInitWindowDelay = 30;
  base::MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      base::Bind(&AppListServiceWin::LoadProfileForWarmup,
                 weak_factory_.GetWeakPtr()),
      base::TimeDelta::FromSeconds(kInitWindowDelay));
}

bool AppListServiceWin::IsWarmupNeeded() {
  if (!g_browser_process || g_browser_process->IsShuttingDown() ||
      browser_shutdown::IsTryingToQuit()) {
    return false;
  }

  // We only need to initialize the view if there's no view already created and
  // there's no profile loading to be shown.
  return !shower_->HasView() && !profile_loader().IsAnyProfileLoading();
}

void AppListServiceWin::LoadProfileForWarmup() {
  if (!IsWarmupNeeded())
    return;

  ProfileManager* profile_manager = g_browser_process->profile_manager();
  base::FilePath profile_path(GetProfilePath(profile_manager->user_data_dir()));

  profile_loader().LoadProfileInvalidatingOtherLoads(
      profile_path,
      base::Bind(&AppListServiceWin::OnLoadProfileForWarmup,
                 weak_factory_.GetWeakPtr()));
}

namespace chrome {

AppListService* GetAppListServiceWin() {
  return AppListServiceWin::GetInstance();
}

}  // namespace chrome

/* [<][>][^][v][top][bottom][index][help] */