background_mode_manager.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base_paths.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/command_line.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_registry_simple.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/prefs/pref_service.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/utf_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/app/chrome_command_ids.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/background/background_application_list_model.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/background/background_mode_manager.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_shutdown.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/lifetime/application_lifetime.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_info_cache.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/status_icons/status_icon.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/status_icons/status_tray.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_commands.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/chrome_pages.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/extensions/application_launch.h"
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_constants.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_notification_types.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/permissions/permission_set.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/user_metrics.h"
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "grit/chrome_unscaled_resources.h"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/chromium_strings.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::UserMetricsAction;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::Extension;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using extensions::UpdatedExtensionPermissionsInfo;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeData::BackgroundModeData(
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id,
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : applications_(new BackgroundApplicationListModel(profile)),
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_id_(command_id),
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_(profile) {
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeData::~BackgroundModeData() {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::BackgroundModeData::IsCommandIdChecked(
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id) const {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED() << "There are no checked items in the profile submenu.";
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::BackgroundModeData::IsCommandIdEnabled(
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id) const {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return command_id != IDC_MinimumLabelValue;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::BackgroundModeData::GetAcceleratorForCommandId(
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id, ui::Accelerator* accelerator) {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No accelerators for status icon context menus.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int item,
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int event_flags) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (item) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_MinimumLabelValue:
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Do nothing. This is just a label.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Launch the app associated with this item.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const Extension* extension = applications_->
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetExtension(item);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BackgroundModeManager::LaunchBackgroundApplication(profile_, extension);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Browser* browser = chrome::FindLastActiveWithProfile(profile_,
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                       host_desktop_type);
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return browser ? browser : chrome::OpenEmptyWindow(profile_,
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                     host_desktop_type);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return applications_->size();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::SimpleMenuModel* menu,
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::SimpleMenuModel* containing_menu) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int position = 0;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When there are no background applications, we want to display
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // just a label stating that none are running.
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (applications_->size() < 1) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    menu->AddItemWithStringId(IDC_MinimumLabelValue,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              IDS_BACKGROUND_APP_NOT_INSTALLED);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (extensions::ExtensionList::const_iterator cursor =
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             applications_->begin();
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         cursor != applications_->end();
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++cursor, ++position) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const gfx::ImageSkia* icon = applications_->GetIcon(*cursor);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(position == applications_->GetPosition(*cursor));
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const std::string& name = (*cursor)->name();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu->AddItem(position, UTF8ToUTF16(name));
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (icon)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        menu->SetIcon(menu->GetItemCount() - 1, gfx::Image(*icon));
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (containing_menu)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    containing_menu->AddSubMenu(command_id_, name_, menu);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::BackgroundModeData::SetName(
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& new_profile_name) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  name_ = new_profile_name;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 BackgroundModeManager::BackgroundModeData::name() {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return name_;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BackgroundModeData* bmd1,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BackgroundModeData* bmd2) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return bmd1->name_ < bmd2->name_;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, public
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeManager(
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CommandLine* command_line,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProfileInfoCache* profile_cache)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : profile_cache_(profile_cache),
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      status_tray_(NULL),
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      status_icon_(NULL),
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      context_menu_(NULL),
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      in_background_mode_(false),
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keep_alive_for_startup_(false),
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keep_alive_for_test_(false),
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_command_id_(0) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should never start up if there is no browser process or if we are
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // currently quitting.
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(g_browser_process != NULL);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(!browser_shutdown::IsTryingToQuit());
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add self as an observer for the profile info cache so we know when profiles
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are deleted and their names change.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  profile_cache_->AddObserver(this);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for the background mode preference changing.
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (g_browser_process->local_state()) {  // Skip for unit tests
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pref_registrar_.Init(g_browser_process->local_state());
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    pref_registrar_.Add(
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        prefs::kBackgroundModeEnabled,
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&BackgroundModeManager::OnBackgroundModeEnabledPrefChanged,
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   base::Unretained(this)));
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Keep the browser alive until extensions are done loading - this is needed
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // by the --no-startup-window flag. We want to stay alive until we load
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // extensions, at which point we should either run in background mode (if
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // there are background apps) or exit if there are none.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_line->HasSwitch(switches::kNoStartupWindow)) {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keep_alive_for_startup_ = true;
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    chrome::StartKeepAlive();
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the -keep-alive-for-test flag is passed, then always keep chrome running
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in the background until the user explicitly terminates it.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_line->HasSwitch(switches::kKeepAliveForTest))
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keep_alive_for_test_ = true;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldBeInBackgroundMode())
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartBackgroundMode();
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for the application shutting down so we can decrement our KeepAlive
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // count.
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::NotificationService::AllSources());
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::~BackgroundModeManager() {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove ourselves from the application observer list (only needed by unit
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tests since APP_TERMINATING is what does this in a real running system).
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::iterator it =
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    it->second->applications_->RemoveObserver(this);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We're going away, so exit background mode (does nothing if we aren't in
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // background mode currently). This is primarily needed for unit tests,
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because in an actual running system we'd get an APP_TERMINATING
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // notification before being destroyed.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndBackgroundMode();
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#if defined(OS_MACOSX)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
230c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::RegisterProfile(Profile* profile) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't want to register multiple times for one profile.
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(background_mode_data_.find(profile) == background_mode_data_.end());
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfo bmd(new BackgroundModeData(current_command_id_++,
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                profile));
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  background_mode_data_[profile] = bmd;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initially set the name for this background mode data.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t index = profile_cache_->GetIndexOfProfileWithPath(profile->GetPath());
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 name = l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME);
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index != std::string::npos)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name = profile_cache_->GetNameOfProfileAtIndex(index);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bmd->SetName(name);
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for when extensions are loaded or add the background permission so
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we can display a "background app installed" notification and enter
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // "launch on login" mode on the Mac.
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check for the presence of background apps after all extensions have been
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // loaded, to handle the case where an extension has been manually removed
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // while Chrome was not running.
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bmd->applications_->AddObserver(this);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're adding a new profile and running in multi-profile mode, this new
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // profile should be added to the status icon if one currently exists.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_ && status_icon_)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::LaunchBackgroundApplication(
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::OpenApplication(chrome::AppLaunchParams(profile, extension,
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                  NEW_FOREGROUND_TAB));
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::IsBackgroundModeActiveForTest() {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return in_background_mode_;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::NumberOfBackgroundModeData() {
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return background_mode_data_.size();
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, content::NotificationObserver overrides
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::Observe(
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSIONS_READY:
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Extensions are loaded, so we don't need to manually keep the browser
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // process alive any more when running in no-startup-window mode.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndKeepAliveForStartup();
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_LOADED: {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Extension* extension = content::Details<Extension>(details).ptr();
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Profile* profile = content::Source<Profile>(source).ptr();
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (BackgroundApplicationListModel::IsBackgroundApp(
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                *extension, profile)) {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Extensions loaded after the ExtensionsService is ready should be
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // treated as new installs.
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (extensions::ExtensionSystem::Get(profile)->extension_service()->
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  is_ready()) {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            OnBackgroundAppInstalled(extension);
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED: {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        UpdatedExtensionPermissionsInfo* info =
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            content::Details<UpdatedExtensionPermissionsInfo>(details).ptr();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (info->permissions->HasAPIPermission(
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                extensions::APIPermission::kBackground) &&
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            info->reason == UpdatedExtensionPermissionsInfo::ADDED) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Turned on background permission, so treat this as a new install.
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          OnBackgroundAppInstalled(info->extension);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_APP_TERMINATING:
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Make sure we aren't still keeping the app alive (only happens if we
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // don't receive an EXTENSIONS_READY notification for some reason).
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndKeepAliveForStartup();
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Performing an explicit shutdown, so exit background mode (does nothing
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // if we aren't in background mode currently).
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndBackgroundMode();
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Shutting down, so don't listen for any more notifications so we don't
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // try to re-enter/exit background mode again.
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      registrar_.RemoveAll();
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (BackgroundModeInfoMap::iterator it =
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               background_mode_data_.begin();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           it != background_mode_data_.end();
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ++it) {
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        it->second->applications_->RemoveObserver(this);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsBackgroundModePrefEnabled())
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableBackgroundMode();
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DisableBackgroundMode();
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnApplicationDataChanged(
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension, Profile* profile) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateStatusTrayIconContextMenu();
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnApplicationListChanged(Profile* profile) {
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsBackgroundModePrefEnabled())
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the profile cache with the fact whether background apps are running
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for this profile.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t profile_index = profile_cache_->GetIndexOfProfileWithPath(
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile->GetPath());
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_index != std::string::npos) {
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile_cache_->SetBackgroundStatusOfProfileAtIndex(
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profile_index, GetBackgroundAppCountForProfile(profile) != 0);
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShouldBeInBackgroundMode()) {
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We've uninstalled our last background app, make sure we exit background
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mode and no longer launch on startup.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableLaunchOnStartup(false);
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndBackgroundMode();
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We have at least one background app running - make sure we're in
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // background mode.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!in_background_mode_) {
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We're entering background mode - make sure we have launch-on-startup
388c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // enabled. On Mac, the platform-specific code tracks whether the user
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // has deleted a login item in the past, and if so, no login item will
390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // be created (to avoid overriding the specific user action).
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EnableLaunchOnStartup(true);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartBackgroundMode();
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // List of applications changed so update the UI.
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, ProfileInfoCacheObserver overrides
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::OnProfileAdded(const base::FilePath& profile_path) {
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileInfoCache& cache =
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 profile_name = cache.GetNameOfProfileAtIndex(
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache.GetIndexOfProfileWithPath(profile_path));
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // At this point, the profile should be registered with the background mode
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // manager, but when it's actually added to the cache is when its name is
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // set so we need up to update that with the background_mode_data.
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::const_iterator it =
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->first->GetPath() == profile_path) {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      it->second->SetName(profile_name);
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UpdateStatusTrayIconContextMenu();
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnProfileWillBeRemoved(
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path) {
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileInfoCache& cache =
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 profile_name = cache.GetNameOfProfileAtIndex(
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache.GetIndexOfProfileWithPath(profile_path));
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove the profile from our map of profiles.
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfoMap::iterator it =
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBackgroundModeIterator(profile_name);
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If a profile isn't running a background app, it may not be in the map.
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != background_mode_data_.end()) {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_mode_data_.erase(it);
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnProfileWasRemoved(
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path,
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& profile_name) {
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnProfileNameChanged(
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path,
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& old_profile_name) {
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileInfoCache& cache =
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 new_profile_name = cache.GetNameOfProfileAtIndex(
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache.GetIndexOfProfileWithPath(profile_path));
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfoMap::const_iterator it =
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBackgroundModeIterator(old_profile_name);
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We check that the returned iterator is valid due to unittests, but really
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this should only be called on profiles already known by the background
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mode manager.
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != background_mode_data_.end()) {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    it->second->SetName(new_profile_name);
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnProfileAvatarChanged(
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path) {
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::IsCommandIdChecked(
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id) const {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::IsCommandIdEnabled(
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id) const {
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND) {
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrefService* service = g_browser_process->local_state();
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(service);
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return command_id != IDC_MinimumLabelValue;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::GetAcceleratorForCommandId(
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int command_id, ui::Accelerator* accelerator) {
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No accelerators for status icon context menus.
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When a browser window is necessary, we use the first profile. The windows
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // opened for these commands are not profile-specific, so any profile would
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // work and the first is convenient.
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeData* bmd = background_mode_data_.begin()->second.get();
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (command_id) {
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_ABOUT:
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::ShowAboutChrome(bmd->GetBrowserWindow());
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_TASK_MANAGER:
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::OpenTaskManager(bmd->GetBrowserWindow(), true);
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_EXIT:
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::RecordAction(UserMetricsAction("Exit"));
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome::AttemptExit();
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: {
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Background mode must already be enabled (as otherwise this menu would
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // not be visible).
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(IsBackgroundModePrefEnabled());
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DCHECK(chrome::WillKeepAlive());
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Set the background mode pref to "disabled" - the resulting notification
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // will result in a call to DisableBackgroundMode().
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrefService* service = g_browser_process->local_state();
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(service);
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      service->SetBoolean(prefs::kBackgroundModeEnabled, false);
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bmd->ExecuteCommand(command_id, event_flags);
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, private
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::EndKeepAliveForStartup() {
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (keep_alive_for_startup_) {
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keep_alive_for_startup_ = false;
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We call this via the message queue to make sure we don't try to end
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // keep-alive (which can shutdown Chrome) before the message loop has
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // started.
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    MessageLoop::current()->PostTask(FROM_HERE,
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     base::Bind(&chrome::EndKeepAlive));
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::StartBackgroundMode() {
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(ShouldBeInBackgroundMode());
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't bother putting ourselves in background mode if we're already there
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // or if background mode is disabled.
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mark ourselves as running in background mode.
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_background_mode_ = true;
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Put ourselves in KeepAlive mode and create a status tray icon.
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::StartKeepAlive();
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Display a status icon to exit Chrome.
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitStatusTrayIcon();
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<BackgroundModeManager>(this),
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Details<bool>(&in_background_mode_));
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::InitStatusTrayIcon() {
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only initialize status tray icons for those profiles which actually
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // have a background app running.
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldBeInBackgroundMode())
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CreateStatusTrayIcon();
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::EndBackgroundMode() {
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!in_background_mode_)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_background_mode_ = false;
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // End KeepAlive mode and blow away our status tray icon.
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::EndKeepAlive();
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveStatusTrayIcon();
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<BackgroundModeManager>(this),
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Details<bool>(&in_background_mode_));
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::EnableBackgroundMode() {
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(IsBackgroundModePrefEnabled());
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If background mode should be enabled, but isn't, turn it on.
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartBackgroundMode();
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableLaunchOnStartup(true);
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::DisableBackgroundMode() {
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!IsBackgroundModePrefEnabled());
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If background mode is currently enabled, turn it off.
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_) {
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndBackgroundMode();
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableLaunchOnStartup(false);
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::GetBackgroundAppCount() const {
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int count = 0;
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Walk the BackgroundModeData for all profiles and count the number of apps.
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::const_iterator it =
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    count += it->second->GetBackgroundAppCount();
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(count >= 0);
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return count;
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::GetBackgroundAppCountForProfile(
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* const profile) const {
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeData* bmd = GetBackgroundModeData(profile);
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return bmd->GetBackgroundAppCount();
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return IsBackgroundModePrefEnabled() &&
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (GetBackgroundAppCount() > 0 || keep_alive_for_test_);
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnBackgroundAppInstalled(
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Background mode is disabled - don't do anything.
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsBackgroundModePrefEnabled())
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check if we need a status tray icon and make one if we do (needed so we
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // can display the app-installed notification below).
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CreateStatusTrayIcon();
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Notify the user that a background app has been installed.
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (extension)  // NULL when called by unit tests.
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DisplayAppInstalledNotification(extension);
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::CreateStatusTrayIcon() {
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Chrome and Mac can use the dock icon instead.
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since there are multiple profiles which share the status tray, we now
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // use the browser process to keep track of it.
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_tray_)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_tray_ = g_browser_process->status_tray();
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the platform doesn't support status icons, or we've already created
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // our status icon, just return.
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_tray_ || status_icon_)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_icon_ = status_tray_->CreateStatusIcon();
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_icon_)
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the image and add ourselves as a click observer on it.
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Status tray icon should have submenus for each profile.
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia* image_skia = ui::ResourceBundle::GetSharedInstance().
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_icon_->SetImage(*image_skia);
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateStatusTrayIconContextMenu();
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If no status icon exists, it's either because one wasn't created when
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // it should have been which can happen when extensions load after the
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // profile has already been registered with the background mode manager.
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_ && !status_icon_)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     CreateStatusTrayIcon();
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we don't have a status icon or one could not be created succesfully,
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // then no need to continue the update.
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_icon_)
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should only get here if we have a profile loaded, or if we're running
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in test mode.
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (background_mode_data_.empty()) {
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(keep_alive_for_test_);
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Add current profile color or indicator.
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create a context menu item for Chrome.
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(this);
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add About item
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddSeparator(ui::NORMAL_SEPARATOR);
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_cache_->GetNumberOfProfiles() > 1) {
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<BackgroundModeData*> bmd_vector;
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (BackgroundModeInfoMap::iterator it =
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         background_mode_data_.begin();
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         it != background_mode_data_.end();
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++it) {
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       bmd_vector.push_back(it->second.get());
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::sort(bmd_vector.begin(), bmd_vector.end(),
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              &BackgroundModeData::BackgroundModeDataCompare);
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int profiles_with_apps = 0;
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (std::vector<BackgroundModeData*>::const_iterator bmd_it =
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         bmd_vector.begin();
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         bmd_it != bmd_vector.end();
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++bmd_it) {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BackgroundModeData* bmd = *bmd_it;
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We should only display the profile in the status icon if it has at
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // least one background app.
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (bmd->GetBackgroundAppCount() > 0) {
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(bmd);
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bmd->BuildProfileMenu(submenu, menu);
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profiles_with_apps++;
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We should only be displaying the status tray icon if there is at least
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // one profile with a background app.
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_GT(profiles_with_apps, 0);
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We should only have one profile in the cache if we are not
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // using multi-profiles. If keep_alive_for_test_ is set, then we may not
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // have any profiles in the cache.
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) ||
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           keep_alive_for_test_);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_mode_data_.begin()->second->BuildProfileMenu(menu, NULL);
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  menu->AddSeparator(ui::NORMAL_SEPARATOR);
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddCheckItemWithStringId(
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
7362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context_menu_ = menu;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_icon_->SetContextMenu(menu);
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::RemoveStatusTrayIcon() {
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_icon_)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_tray_->RemoveStatusIcon(status_icon_);
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_icon_ = NULL;
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context_menu_ = NULL;
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeData*
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::GetBackgroundModeData(Profile* const profile) const {
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(background_mode_data_.find(profile) != background_mode_data_.end());
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return background_mode_data_.find(profile)->second.get();
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeInfoMap::iterator
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::GetBackgroundModeIterator(
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& profile_name) {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfoMap::iterator profile_it =
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      background_mode_data_.end();
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::iterator it =
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->second->name() == profile_name) {
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_it = it;
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return profile_it;
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefService* service = g_browser_process->local_state();
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(service);
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return service->GetBoolean(prefs::kBackgroundModeEnabled);
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775