background_mode_manager.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/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"
217dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/lifetime/application_lifetime.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_info_cache.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/status_icons/status_icon.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/status_icons/status_tray.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_commands.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/chrome_pages.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/extensions/application_launch.h"
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_constants.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)///////////////////////////////////////////////////////////////////////////////
66424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)//  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::BackgroundModeData::ExecuteCommand(
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int item,
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int event_flags) {
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (item) {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_MinimumLabelValue:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Do nothing. This is just a label.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Launch the app associated with this item.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const Extension* extension = applications_->
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetExtension(item);
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BackgroundModeManager::LaunchBackgroundApplication(profile_, extension);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Browser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Browser* browser = chrome::FindLastActiveWithProfile(profile_,
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                       host_desktop_type);
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return browser ? browser : chrome::OpenEmptyWindow(profile_,
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                     host_desktop_type);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return applications_->size();
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
96424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    StatusIconMenuModel* menu,
97424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    StatusIconMenuModel* containing_menu) {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int position = 0;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When there are no background applications, we want to display
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // just a label stating that none are running.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (applications_->size() < 1) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    menu->AddItemWithStringId(IDC_MinimumLabelValue,
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              IDS_BACKGROUND_APP_NOT_INSTALLED);
104424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (extensions::ExtensionList::const_iterator cursor =
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             applications_->begin();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         cursor != applications_->end();
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++cursor, ++position) {
110868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      const gfx::ImageSkia* icon = applications_->GetIcon(cursor->get());
111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      DCHECK(position == applications_->GetPosition(cursor->get()));
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const std::string& name = (*cursor)->name();
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu->AddItem(position, UTF8ToUTF16(name));
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (icon)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        menu->SetIcon(menu->GetItemCount() - 1, gfx::Image(*icon));
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (containing_menu)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    containing_menu->AddSubMenu(command_id_, name_, menu);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::BackgroundModeData::SetName(
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& new_profile_name) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  name_ = new_profile_name;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)string16 BackgroundModeManager::BackgroundModeData::name() {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return name_;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BackgroundModeData* bmd1,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const BackgroundModeData* bmd2) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return bmd1->name_ < bmd2->name_;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, public
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeManager(
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CommandLine* command_line,
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ProfileInfoCache* profile_cache)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : profile_cache_(profile_cache),
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      status_tray_(NULL),
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      status_icon_(NULL),
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      context_menu_(NULL),
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      in_background_mode_(false),
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keep_alive_for_startup_(false),
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keep_alive_for_test_(false),
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_command_id_(0) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should never start up if there is no browser process or if we are
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // currently quitting.
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(g_browser_process != NULL);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(!browser_shutdown::IsTryingToQuit());
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add self as an observer for the profile info cache so we know when profiles
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are deleted and their names change.
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  profile_cache_->AddObserver(this);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for the background mode preference changing.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (g_browser_process->local_state()) {  // Skip for unit tests
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pref_registrar_.Init(g_browser_process->local_state());
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    pref_registrar_.Add(
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        prefs::kBackgroundModeEnabled,
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&BackgroundModeManager::OnBackgroundModeEnabledPrefChanged,
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   base::Unretained(this)));
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Keep the browser alive until extensions are done loading - this is needed
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // by the --no-startup-window flag. We want to stay alive until we load
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // extensions, at which point we should either run in background mode (if
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // there are background apps) or exit if there are none.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_line->HasSwitch(switches::kNoStartupWindow)) {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keep_alive_for_startup_ = true;
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    chrome::StartKeepAlive();
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the -keep-alive-for-test flag is passed, then always keep chrome running
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in the background until the user explicitly terminates it.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_line->HasSwitch(switches::kKeepAliveForTest))
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keep_alive_for_test_ = true;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldBeInBackgroundMode())
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartBackgroundMode();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for the application shutting down so we can decrement our KeepAlive
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // count.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::NotificationService::AllSources());
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::~BackgroundModeManager() {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove ourselves from the application observer list (only needed by unit
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tests since APP_TERMINATING is what does this in a real running system).
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::iterator it =
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    it->second->applications_->RemoveObserver(this);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We're going away, so exit background mode (does nothing if we aren't in
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // background mode currently). This is primarily needed for unit tests,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // because in an actual running system we'd get an APP_TERMINATING
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // notification before being destroyed.
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndBackgroundMode();
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#if defined(OS_MACOSX)
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::RegisterProfile(Profile* profile) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't want to register multiple times for one profile.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(background_mode_data_.find(profile) == background_mode_data_.end());
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfo bmd(new BackgroundModeData(current_command_id_++,
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                profile));
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  background_mode_data_[profile] = bmd;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initially set the name for this background mode data.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t index = profile_cache_->GetIndexOfProfileWithPath(profile->GetPath());
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 name = l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index != std::string::npos)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name = profile_cache_->GetNameOfProfileAtIndex(index);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bmd->SetName(name);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Listen for when extensions are loaded or add the background permission so
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we can display a "background app installed" notification and enter
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // "launch on login" mode on the Mac.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check for the presence of background apps after all extensions have been
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // loaded, to handle the case where an extension has been manually removed
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // while Chrome was not running.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bmd->applications_->AddObserver(this);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we're adding a new profile and running in multi-profile mode, this new
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // profile should be added to the status icon if one currently exists.
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_ && status_icon_)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::LaunchBackgroundApplication(
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile,
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
2624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  OpenApplication(AppLaunchParams(profile, extension, NEW_FOREGROUND_TAB));
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::IsBackgroundModeActiveForTest() {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return in_background_mode_;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::NumberOfBackgroundModeData() {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return background_mode_data_.size();
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, content::NotificationObserver overrides
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::Observe(
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSIONS_READY:
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Extensions are loaded, so we don't need to manually keep the browser
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // process alive any more when running in no-startup-window mode.
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndKeepAliveForStartup();
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_LOADED: {
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Extension* extension = content::Details<Extension>(details).ptr();
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Profile* profile = content::Source<Profile>(source).ptr();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (BackgroundApplicationListModel::IsBackgroundApp(
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                *extension, profile)) {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Extensions loaded after the ExtensionsService is ready should be
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // treated as new installs.
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          if (extensions::ExtensionSystem::Get(profile)->extension_service()->
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                  is_ready()) {
295eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            bool is_being_reloaded = false;
296eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            CheckReloadStatus(extension, &is_being_reloaded);
297eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            // No need to show the notification if we showed to the user
298eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            // previously for this app.
299eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            if (!is_being_reloaded)
300eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch              OnBackgroundAppInstalled(extension);
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED: {
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        UpdatedExtensionPermissionsInfo* info =
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            content::Details<UpdatedExtensionPermissionsInfo>(details).ptr();
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (info->permissions->HasAPIPermission(
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                extensions::APIPermission::kBackground) &&
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            info->reason == UpdatedExtensionPermissionsInfo::ADDED) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          // Turned on background permission, so treat this as a new install.
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          OnBackgroundAppInstalled(info->extension);
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_APP_TERMINATING:
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Make sure we aren't still keeping the app alive (only happens if we
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // don't receive an EXTENSIONS_READY notification for some reason).
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndKeepAliveForStartup();
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Performing an explicit shutdown, so exit background mode (does nothing
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // if we aren't in background mode currently).
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndBackgroundMode();
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Shutting down, so don't listen for any more notifications so we don't
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // try to re-enter/exit background mode again.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      registrar_.RemoveAll();
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (BackgroundModeInfoMap::iterator it =
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               background_mode_data_.begin();
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           it != background_mode_data_.end();
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ++it) {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        it->second->applications_->RemoveObserver(this);
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsBackgroundModePrefEnabled())
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableBackgroundMode();
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DisableBackgroundMode();
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnApplicationDataChanged(
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension, Profile* profile) {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateStatusTrayIconContextMenu();
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnApplicationListChanged(Profile* profile) {
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsBackgroundModePrefEnabled())
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update the profile cache with the fact whether background apps are running
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for this profile.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t profile_index = profile_cache_->GetIndexOfProfileWithPath(
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile->GetPath());
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_index != std::string::npos) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    profile_cache_->SetBackgroundStatusOfProfileAtIndex(
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profile_index, GetBackgroundAppCountForProfile(profile) != 0);
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ShouldBeInBackgroundMode()) {
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We've uninstalled our last background app, make sure we exit background
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mode and no longer launch on startup.
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableLaunchOnStartup(false);
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndBackgroundMode();
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We have at least one background app running - make sure we're in
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // background mode.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!in_background_mode_) {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We're entering background mode - make sure we have launch-on-startup
376c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // enabled. On Mac, the platform-specific code tracks whether the user
377c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // has deleted a login item in the past, and if so, no login item will
378c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // be created (to avoid overriding the specific user action).
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EnableLaunchOnStartup(true);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartBackgroundMode();
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // List of applications changed so update the UI.
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, ProfileInfoCacheObserver overrides
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::OnProfileAdded(const base::FilePath& profile_path) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileInfoCache& cache =
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 profile_name = cache.GetNameOfProfileAtIndex(
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache.GetIndexOfProfileWithPath(profile_path));
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // At this point, the profile should be registered with the background mode
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // manager, but when it's actually added to the cache is when its name is
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // set so we need up to update that with the background_mode_data.
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::const_iterator it =
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->first->GetPath() == profile_path) {
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      it->second->SetName(profile_name);
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      UpdateStatusTrayIconContextMenu();
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnProfileWillBeRemoved(
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path) {
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileInfoCache& cache =
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 profile_name = cache.GetNameOfProfileAtIndex(
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache.GetIndexOfProfileWithPath(profile_path));
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove the profile from our map of profiles.
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfoMap::iterator it =
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBackgroundModeIterator(profile_name);
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If a profile isn't running a background app, it may not be in the map.
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != background_mode_data_.end()) {
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_mode_data_.erase(it);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnProfileNameChanged(
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& profile_path,
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& old_profile_name) {
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ProfileInfoCache& cache =
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      g_browser_process->profile_manager()->GetProfileInfoCache();
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 new_profile_name = cache.GetNameOfProfileAtIndex(
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      cache.GetIndexOfProfileWithPath(profile_path));
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfoMap::const_iterator it =
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetBackgroundModeIterator(old_profile_name);
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We check that the returned iterator is valid due to unittests, but really
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this should only be called on profiles already known by the background
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mode manager.
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != background_mode_data_.end()) {
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    it->second->SetName(new_profile_name);
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UpdateStatusTrayIconContextMenu();
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
445424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)//  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When a browser window is necessary, we use the first profile. The windows
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // opened for these commands are not profile-specific, so any profile would
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // work and the first is convenient.
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeData* bmd = background_mode_data_.begin()->second.get();
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (command_id) {
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_ABOUT:
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::ShowAboutChrome(bmd->GetBrowserWindow());
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_TASK_MANAGER:
456eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      chrome::OpenTaskManager(bmd->GetBrowserWindow());
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_EXIT:
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::RecordAction(UserMetricsAction("Exit"));
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome::AttemptExit();
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: {
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Background mode must already be enabled (as otherwise this menu would
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // not be visible).
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(IsBackgroundModePrefEnabled());
4662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DCHECK(chrome::WillKeepAlive());
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Set the background mode pref to "disabled" - the resulting notification
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // will result in a call to DisableBackgroundMode().
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      PrefService* service = g_browser_process->local_state();
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DCHECK(service);
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      service->SetBoolean(prefs::kBackgroundModeEnabled, false);
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      bmd->ExecuteCommand(command_id, event_flags);
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  BackgroundModeManager, private
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::EndKeepAliveForStartup() {
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (keep_alive_for_startup_) {
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keep_alive_for_startup_ = false;
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We call this via the message queue to make sure we don't try to end
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // keep-alive (which can shutdown Chrome) before the message loop has
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // started.
49090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::MessageLoop::current()->PostTask(FROM_HERE,
49190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                           base::Bind(&chrome::EndKeepAlive));
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::StartBackgroundMode() {
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(ShouldBeInBackgroundMode());
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't bother putting ourselves in background mode if we're already there
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // or if background mode is disabled.
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_)
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mark ourselves as running in background mode.
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_background_mode_ = true;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Put ourselves in KeepAlive mode and create a status tray icon.
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::StartKeepAlive();
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Display a status icon to exit Chrome.
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitStatusTrayIcon();
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<BackgroundModeManager>(this),
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Details<bool>(&in_background_mode_));
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::InitStatusTrayIcon() {
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only initialize status tray icons for those profiles which actually
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // have a background app running.
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldBeInBackgroundMode())
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CreateStatusTrayIcon();
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::EndBackgroundMode() {
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!in_background_mode_)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_background_mode_ = false;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // End KeepAlive mode and blow away our status tray icon.
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::EndKeepAlive();
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveStatusTrayIcon();
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content::NotificationService::current()->Notify(
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<BackgroundModeManager>(this),
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Details<bool>(&in_background_mode_));
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::EnableBackgroundMode() {
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(IsBackgroundModePrefEnabled());
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If background mode should be enabled, but isn't, turn it on.
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartBackgroundMode();
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableLaunchOnStartup(true);
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::DisableBackgroundMode() {
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!IsBackgroundModePrefEnabled());
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If background mode is currently enabled, turn it off.
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_) {
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndBackgroundMode();
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EnableLaunchOnStartup(false);
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::GetBackgroundAppCount() const {
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int count = 0;
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Walk the BackgroundModeData for all profiles and count the number of apps.
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::const_iterator it =
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    count += it->second->GetBackgroundAppCount();
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(count >= 0);
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return count;
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int BackgroundModeManager::GetBackgroundAppCountForProfile(
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* const profile) const {
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeData* bmd = GetBackgroundModeData(profile);
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return bmd->GetBackgroundAppCount();
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return IsBackgroundModePrefEnabled() &&
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (GetBackgroundAppCount() > 0 || keep_alive_for_test_);
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::OnBackgroundAppInstalled(
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const Extension* extension) {
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Background mode is disabled - don't do anything.
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsBackgroundModePrefEnabled())
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check if we need a status tray icon and make one if we do (needed so we
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // can display the app-installed notification below).
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CreateStatusTrayIcon();
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Notify the user that a background app has been installed.
592eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (extension) {  // NULL when called by unit tests.
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DisplayAppInstalledNotification(extension);
594eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
595eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
596eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
597eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid BackgroundModeManager::CheckReloadStatus(
598eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const Extension* extension,
599eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    bool* is_being_reloaded) {
600eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Walk the BackgroundModeData for all profiles to see if one of their
601eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // extensions is being reloaded.
602eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    for (BackgroundModeInfoMap::const_iterator it =
603eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch             background_mode_data_.begin();
604eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch         it != background_mode_data_.end();
605eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch         ++it) {
606eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      Profile* profile = it->first;
607eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      // If the extension is being reloaded, no need to show a notification.
608eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (profile->GetExtensionService()->IsBeingReloaded(extension->id()))
609eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        *is_being_reloaded = true;
610eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::CreateStatusTrayIcon() {
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Chrome and Mac can use the dock icon instead.
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since there are multiple profiles which share the status tray, we now
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // use the browser process to keep track of it.
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_tray_)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_tray_ = g_browser_process->status_tray();
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the platform doesn't support status icons, or we've already created
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // our status icon, just return.
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_tray_ || status_icon_)
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Status tray icon should have submenus for each profile.
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia* image_skia = ui::ResourceBundle::GetSharedInstance().
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
632a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
633a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  status_icon_ = status_tray_->CreateStatusIcon(
634a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      StatusTray::BACKGROUND_MODE_ICON,
635a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      *image_skia,
636a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
637a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (!status_icon_)
638a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return;
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateStatusTrayIconContextMenu();
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If no status icon exists, it's either because one wasn't created when
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // it should have been which can happen when extensions load after the
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // profile has already been registered with the background mode manager.
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (in_background_mode_ && !status_icon_)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     CreateStatusTrayIcon();
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we don't have a status icon or one could not be created succesfully,
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // then no need to continue the update.
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!status_icon_)
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should only get here if we have a profile loaded, or if we're running
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in test mode.
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (background_mode_data_.empty()) {
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(keep_alive_for_test_);
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rlp): Add current profile color or indicator.
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create a context menu item for Chrome.
663424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add About item
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddSeparator(ui::NORMAL_SEPARATOR);
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (profile_cache_->GetNumberOfProfiles() > 1) {
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<BackgroundModeData*> bmd_vector;
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (BackgroundModeInfoMap::iterator it =
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         background_mode_data_.begin();
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         it != background_mode_data_.end();
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++it) {
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       bmd_vector.push_back(it->second.get());
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::sort(bmd_vector.begin(), bmd_vector.end(),
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              &BackgroundModeData::BackgroundModeDataCompare);
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int profiles_with_apps = 0;
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (std::vector<BackgroundModeData*>::const_iterator bmd_it =
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         bmd_vector.begin();
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         bmd_it != bmd_vector.end();
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         ++bmd_it) {
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BackgroundModeData* bmd = *bmd_it;
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We should only display the profile in the status icon if it has at
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // least one background app.
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (bmd->GetBackgroundAppCount() > 0) {
688424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        StatusIconMenuModel* submenu = new StatusIconMenuModel(bmd);
689424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)        bmd->BuildProfileMenu(submenu, menu.get());
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        profiles_with_apps++;
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We should only be displaying the status tray icon if there is at least
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // one profile with a background app.
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_GT(profiles_with_apps, 0);
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We should only have one profile in the cache if we are not
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // using multi-profiles. If keep_alive_for_test_ is set, then we may not
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // have any profiles in the cache.
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) ||
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           keep_alive_for_test_);
702424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    background_mode_data_.begin()->second->BuildProfileMenu(menu.get(), NULL);
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  menu->AddSeparator(ui::NORMAL_SEPARATOR);
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddCheckItemWithStringId(
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
709424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
710424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                            true);
711424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
712424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  PrefService* service = g_browser_process->local_state();
713424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  DCHECK(service);
714424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  bool enabled =
715424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
716424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
717424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                            enabled);
718424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
7202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
721424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  context_menu_ = menu.get();
722424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  status_icon_->SetContextMenu(menu.Pass());
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BackgroundModeManager::RemoveStatusTrayIcon() {
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_icon_)
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    status_tray_->RemoveStatusIcon(status_icon_);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  status_icon_ = NULL;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  context_menu_ = NULL;
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeData*
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::GetBackgroundModeData(Profile* const profile) const {
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(background_mode_data_.find(profile) != background_mode_data_.end());
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return background_mode_data_.find(profile)->second.get();
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::BackgroundModeInfoMap::iterator
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BackgroundModeManager::GetBackgroundModeIterator(
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const string16& profile_name) {
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BackgroundModeInfoMap::iterator profile_it =
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      background_mode_data_.end();
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (BackgroundModeInfoMap::iterator it =
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       background_mode_data_.begin();
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != background_mode_data_.end();
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++it) {
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (it->second->name() == profile_name) {
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      profile_it = it;
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return profile_it;
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrefService* service = g_browser_process->local_state();
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(service);
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return service->GetBoolean(prefs::kBackgroundModeEnabled);
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
759