background_mode_manager.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
151125a21eafc29c925cac3655b46cfd8ef55f764Ted Kremenek// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a11a5f13aec18eb8c076bc2c3249afc932c921aTed Kremenek// Use of this source code is governed by a BSD-style license that can be
32a11a5f13aec18eb8c076bc2c3249afc932c921aTed Kremenek// found in the LICENSE file.
42a11a5f13aec18eb8c076bc2c3249afc932c921aTed Kremenek
50bc735ffcfb223c0186419547abaa5c84482663eChris Lattner#include <algorithm>
60bc735ffcfb223c0186419547abaa5c84482663eChris Lattner#include <string>
72a11a5f13aec18eb8c076bc2c3249afc932c921aTed Kremenek#include <vector>
82a11a5f13aec18eb8c076bc2c3249afc932c921aTed Kremenek
92a11a5f13aec18eb8c076bc2c3249afc932c921aTed Kremenek#include "base/base_paths.h"
1051125a21eafc29c925cac3655b46cfd8ef55f764Ted Kremenek#include "base/bind.h"
1151125a21eafc29c925cac3655b46cfd8ef55f764Ted Kremenek#include "base/command_line.h"
12b2213dc3dd8f58b611b91d2fce4834a767efcba7Jeffrey Yasskin#include "base/logging.h"
13b2213dc3dd8f58b611b91d2fce4834a767efcba7Jeffrey Yasskin#include "base/prefs/pref_registry_simple.h"
14b2213dc3dd8f58b611b91d2fce4834a767efcba7Jeffrey Yasskin#include "base/prefs/pref_service.h"
15b2213dc3dd8f58b611b91d2fce4834a767efcba7Jeffrey Yasskin#include "base/strings/utf_string_conversions.h"
164241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/app/chrome_command_ids.h"
174241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/browser/background/background_application_list_model.h"
184241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/browser/background/background_mode_manager.h"
195a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis#include "chrome/browser/browser_process.h"
205a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis#include "chrome/browser/browser_shutdown.h"
214241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/browser/chrome_notification_types.h"
2263bbe5312cd89ce0ceb684bff68c5baef636e93cTed Kremenek#include "chrome/browser/extensions/extension_service.h"
2330a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/extensions/extension_system.h"
2430a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/lifetime/application_lifetime.h"
2530a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/profiles/profile.h"
2630a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/profiles/profile_info_cache.h"
2730a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/profiles/profile_manager.h"
284241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/browser/status_icons/status_icon.h"
2930a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/status_icons/status_tray.h"
30f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek#include "chrome/browser/ui/browser.h"
3130a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/ui/browser_commands.h"
324241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/browser/ui/browser_finder.h"
3363bbe5312cd89ce0ceb684bff68c5baef636e93cTed Kremenek#include "chrome/browser/ui/browser_list.h"
34651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines#include "chrome/browser/ui/chrome_pages.h"
35626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek#include "chrome/browser/ui/extensions/application_launch.h"
364241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/browser/ui/host_desktop.h"
374241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek#include "chrome/common/chrome_constants.h"
384a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek#include "chrome/common/chrome_switches.h"
3911062b118476368fa5b294954713e5df97d8599fTed Kremenek#include "chrome/common/extensions/extension.h"
405a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis#include "chrome/common/extensions/extension_constants.h"
419ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenek#include "chrome/common/pref_names.h"
425a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis#include "content/public/browser/notification_service.h"
435fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek#include "content/public/browser/user_metrics.h"
4411062b118476368fa5b294954713e5df97d8599fTed Kremenek#include "extensions/common/permissions/permission_set.h"
45cf118d41f7930a18dce97416ef7834a62642f587Ted Kremenek#include "grit/chrome_unscaled_resources.h"
46cf118d41f7930a18dce97416ef7834a62642f587Ted Kremenek#include "grit/chromium_strings.h"
47cf118d41f7930a18dce97416ef7834a62642f587Ted Kremenek#include "grit/generated_resources.h"
48cf118d41f7930a18dce97416ef7834a62642f587Ted Kremenek#include "ui/base/l10n/l10n_util.h"
49cf118d41f7930a18dce97416ef7834a62642f587Ted Kremenek#include "ui/base/resource/resource_bundle.h"
501eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
51bda1efd0daf6fca9f515c6ce38d1ed71a3cca5b7Zhongxing Xuusing content::UserMetricsAction;
52bda1efd0daf6fca9f515c6ce38d1ed71a3cca5b7Zhongxing Xuusing extensions::Extension;
53bda1efd0daf6fca9f515c6ce38d1ed71a3cca5b7Zhongxing Xuusing extensions::UpdatedExtensionPermissionsInfo;
54c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu
5538b02b912e1a55c912f603c4369431264d36a381Zhongxing XuBackgroundModeManager::BackgroundModeData::BackgroundModeData(
56d2592a34a059e7cbb2b11dc53649ac4912422909Argyrios Kyrtzidis    int command_id,
57a19f4af7a94835ce4693bfe12d6270754e79eb56Anna Zaks    Profile* profile)
58d2592a34a059e7cbb2b11dc53649ac4912422909Argyrios Kyrtzidis    : applications_(new BackgroundApplicationListModel(profile)),
59d2592a34a059e7cbb2b11dc53649ac4912422909Argyrios Kyrtzidis      command_id_(command_id),
60d2592a34a059e7cbb2b11dc53649ac4912422909Argyrios Kyrtzidis      profile_(profile) {
61e36de1fe51c39d9161915dd3dbef880954af6476Ted Kremenek}
621eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
631833d284346b9fa11aae4e6aa07381347c04745cJordan RoseBackgroundModeManager::BackgroundModeData::~BackgroundModeData() {
641833d284346b9fa11aae4e6aa07381347c04745cJordan Rose}
651833d284346b9fa11aae4e6aa07381347c04745cJordan Rose
661833d284346b9fa11aae4e6aa07381347c04745cJordan Rose///////////////////////////////////////////////////////////////////////////////
671833d284346b9fa11aae4e6aa07381347c04745cJordan Rose//  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
681833d284346b9fa11aae4e6aa07381347c04745cJordan Rosevoid BackgroundModeManager::BackgroundModeData::ExecuteCommand(
691833d284346b9fa11aae4e6aa07381347c04745cJordan Rose    int item,
701833d284346b9fa11aae4e6aa07381347c04745cJordan Rose    int event_flags) {
711833d284346b9fa11aae4e6aa07381347c04745cJordan Rose  switch (item) {
724c4cb527a44037d076da82ad9d12b4e655e64dbbTed Kremenek    case IDC_MinimumLabelValue:
7346e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose      // Do nothing. This is just a label.
7446e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose      break;
7546e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose    default:
761833d284346b9fa11aae4e6aa07381347c04745cJordan Rose      // Launch the app associated with this item.
774c4cb527a44037d076da82ad9d12b4e655e64dbbTed Kremenek      const Extension* extension = applications_->
78d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek          GetExtension(item);
794c4cb527a44037d076da82ad9d12b4e655e64dbbTed Kremenek      BackgroundModeManager::LaunchBackgroundApplication(profile_, extension);
8046e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose      break;
8146e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose  }
8246e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose}
831eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
8446e778145c56cd9b42cb399795a294b29cb78b62Jordan RoseBrowser* BackgroundModeManager::BackgroundModeData::GetBrowserWindow() {
851eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  chrome::HostDesktopType host_desktop_type = chrome::GetActiveDesktop();
8646e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose  Browser* browser = chrome::FindLastActiveWithProfile(profile_,
871eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump                                                       host_desktop_type);
885d5480380d7b7c3590a0283ddf239220e514e576Ted Kremenek  return browser ? browser : chrome::OpenEmptyWindow(profile_,
891eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump                                                     host_desktop_type);
9046e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose}
911eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
921833d284346b9fa11aae4e6aa07381347c04745cJordan Roseint BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const {
931833d284346b9fa11aae4e6aa07381347c04745cJordan Rose  return applications_->size();
941833d284346b9fa11aae4e6aa07381347c04745cJordan Rose}
959c378f705405d37f49795d5e915989de774fe11fTed Kremenek
961eb4433ac451dc16f4133a88af2d002ac26c58efMike Stumpvoid BackgroundModeManager::BackgroundModeData::BuildProfileMenu(
971833d284346b9fa11aae4e6aa07381347c04745cJordan Rose    StatusIconMenuModel* menu,
981833d284346b9fa11aae4e6aa07381347c04745cJordan Rose    StatusIconMenuModel* containing_menu) {
991833d284346b9fa11aae4e6aa07381347c04745cJordan Rose  int position = 0;
1001833d284346b9fa11aae4e6aa07381347c04745cJordan Rose  // When there are no background applications, we want to display
1011833d284346b9fa11aae4e6aa07381347c04745cJordan Rose  // just a label stating that none are running.
102d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  if (applications_->size() < 1) {
103d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek    menu->AddItemWithStringId(IDC_MinimumLabelValue,
1041833d284346b9fa11aae4e6aa07381347c04745cJordan Rose                              IDS_BACKGROUND_APP_NOT_INSTALLED);
105b38911f16b4943548db6a3695fc6ae23070b25d2Ted Kremenek    menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false);
10646e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose  } else {
107f24af5bc2e01ca8e7396ed997378a77fddfa521eTed Kremenek    for (extensions::ExtensionList::const_iterator cursor =
1081eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump             applications_->begin();
1091eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump         cursor != applications_->end();
1104a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek         ++cursor, ++position) {
1114a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      const gfx::ImageSkia* icon = applications_->GetIcon(cursor->get());
1124a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      DCHECK(position == applications_->GetPosition(cursor->get()));
1131eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      const std::string& name = (*cursor)->name();
1144323a57627e796dcfdfdb7d47672dc09ed308edaTed Kremenek      menu->AddItem(position, UTF8ToUTF16(name));
1158bef8238181a30e52dea380789a7e2d760eac532Ted Kremenek      if (icon)
1161eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump        menu->SetIcon(menu->GetItemCount() - 1, gfx::Image(*icon));
1174a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    }
1184c4cb527a44037d076da82ad9d12b4e655e64dbbTed Kremenek  }
1191eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  if (containing_menu)
1204a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    containing_menu->AddSubMenu(command_id_, name_, menu);
1214c4cb527a44037d076da82ad9d12b4e655e64dbbTed Kremenek}
122c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu
1234a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenekvoid BackgroundModeManager::BackgroundModeData::SetName(
124c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu    const string16& new_profile_name) {
1255204d9e2fe0ea4e4b9c85087e355021c93221764Jordan Rose  name_ = new_profile_name;
1266800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks}
12746e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose
12846e778145c56cd9b42cb399795a294b29cb78b62Jordan Rosestring16 BackgroundModeManager::BackgroundModeData::name() {
129e40b69de464bc695afcaf7ef9602ad727d77b981Ted Kremenek  return name_;
130e40b69de464bc695afcaf7ef9602ad727d77b981Ted Kremenek}
131a5888f61be9f8d76e9b48a453dbced50523bd2e0Argyrios Kyrtzidis
132c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu// static
1334a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenekbool BackgroundModeManager::BackgroundModeData::BackgroundModeDataCompare(
1340f9063c116b7c3b05d8042b5976463c2dae04861Ted Kremenek    const BackgroundModeData* bmd1,
135c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu    const BackgroundModeData* bmd2) {
1361eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  return bmd1->name_ < bmd2->name_;
1371eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump}
13825e695b2d574d919cc1bbddf3a2efe073d449b1cZhongxing Xu
13925e695b2d574d919cc1bbddf3a2efe073d449b1cZhongxing Xu
140955cd444f445bcdbade1cdd3926254c8ee7890d8Anna Zaks///////////////////////////////////////////////////////////////////////////////
141c7ecc43c33a21b82c49664910b19fcc1f555aa51Anna Zaks//  BackgroundModeManager, public
142c7ecc43c33a21b82c49664910b19fcc1f555aa51Anna ZaksBackgroundModeManager::BackgroundModeManager(
143c7ecc43c33a21b82c49664910b19fcc1f555aa51Anna Zaks    CommandLine* command_line,
1445032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu    ProfileInfoCache* profile_cache)
1455032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu    : profile_cache_(profile_cache),
1465032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu      status_tray_(NULL),
1475032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu      status_icon_(NULL),
148b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu      context_menu_(NULL),
149b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu      in_background_mode_(false),
150a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      keep_alive_for_startup_(false),
151a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      keep_alive_for_test_(false),
152a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      background_mode_suspended_(false),
153c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu      keeping_alive_(false),
154c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu      current_command_id_(0) {
1550a6e09f67c719c318856be19d57e19972101f62cJordan Rose  // We should never start up if there is no browser process or if we are
156b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  // currently quitting.
15752c3196a89a26cebcf069dd140c3396b743b8e33Ted Kremenek  CHECK(g_browser_process != NULL);
1587a95de68c093991047ed8d339479ccad51b88663David Blaikie  CHECK(!browser_shutdown::IsTryingToQuit());
1597a95de68c093991047ed8d339479ccad51b88663David Blaikie
160dcd42fbb418cf662c136cb035e235a44b58ad91eJordan Rose  // Add self as an observer for the profile info cache so we know when profiles
161dcd42fbb418cf662c136cb035e235a44b58ad91eJordan Rose  // are deleted and their names change.
1621eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  profile_cache_->AddObserver(this);
1636800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks
1641831bd29572b6a7243da73d9606209190c0217deBenjamin Kramer  // Listen for the background mode preference changing.
1656800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks  if (g_browser_process->local_state()) {  // Skip for unit tests
1665fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek    pref_registrar_.Init(g_browser_process->local_state());
167ef8225444452a1486bd721f3285301fe84643b00Stephen Hines    pref_registrar_.Add(
1686800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks        prefs::kBackgroundModeEnabled,
1695fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek        base::Bind(&BackgroundModeManager::OnBackgroundModeEnabledPrefChanged,
170c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu                   base::Unretained(this)));
171c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  }
172fbe4d36f1f83ca12b532e0a946cbffcdb54f904cJordan Rose
173fbe4d36f1f83ca12b532e0a946cbffcdb54f904cJordan Rose  // Keep the browser alive until extensions are done loading - this is needed
174c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  // by the --no-startup-window flag. We want to stay alive until we load
175c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  // extensions, at which point we should either run in background mode (if
1761eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // there are background apps) or exit if there are none.
177c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  if (command_line->HasSwitch(switches::kNoStartupWindow)) {
1789c378f705405d37f49795d5e915989de774fe11fTed Kremenek    keep_alive_for_startup_ = true;
179c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu    chrome::StartKeepAlive();
1804a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  } else {
1814a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    // Otherwise, start with background mode suspended in case we're launching
1824a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    // in a mode that doesn't open a browser window. It will be resumed when the
1830e8a3c743b9b3e3039e329a1736122d3b5b5fed9Ted Kremenek    // first browser window is opened.
1841eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump    SuspendBackgroundMode();
185ffe0f43806d4823271c2406c1fccc2373115c36aTed Kremenek  }
1864a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek
187cacdbc97d11d2bbde00a63dace6ac26f4b12ed88Craig Topper  // If the -keep-alive-for-test flag is passed, then always keep chrome running
1885903a373db3d27794c90b25687e0dd6adb0e497dAnna Zaks  // in the background until the user explicitly terminates it.
1895903a373db3d27794c90b25687e0dd6adb0e497dAnna Zaks  if (command_line->HasSwitch(switches::kKeepAliveForTest))
1905903a373db3d27794c90b25687e0dd6adb0e497dAnna Zaks    keep_alive_for_test_ = true;
1919c378f705405d37f49795d5e915989de774fe11fTed Kremenek
1926bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines  if (ShouldBeInBackgroundMode())
19352c3196a89a26cebcf069dd140c3396b743b8e33Ted Kremenek    StartBackgroundMode();
1941eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1959c378f705405d37f49795d5e915989de774fe11fTed Kremenek  // Listen for the application shutting down so we can decrement our KeepAlive
19652c3196a89a26cebcf069dd140c3396b743b8e33Ted Kremenek  // count.
19752c3196a89a26cebcf069dd140c3396b743b8e33Ted Kremenek  registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
1981eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump                 content::NotificationService::AllSources());
1990f8579274a010f360a371b53101859d9d6052314Anna Zaks  BrowserList::AddObserver(this);
2006bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines}
2010f8579274a010f360a371b53101859d9d6052314Anna Zaks
2020f8579274a010f360a371b53101859d9d6052314Anna ZaksBackgroundModeManager::~BackgroundModeManager() {
2034a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // Remove ourselves from the application observer list (only needed by unit
20446e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose  // tests since APP_TERMINATING is what does this in a real running system).
2053148eb4a75f70f2636075c364d03104223f004d3Ted Kremenek  for (BackgroundModeInfoMap::iterator it =
20646e778145c56cd9b42cb399795a294b29cb78b62Jordan Rose       background_mode_data_.begin();
2073148eb4a75f70f2636075c364d03104223f004d3Ted Kremenek       it != background_mode_data_.end();
2084a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek       ++it) {
209c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu    it->second->applications_->RemoveObserver(this);
210c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  }
2114a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  BrowserList::RemoveObserver(this);
2124a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek
2134a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // We're going away, so exit background mode (does nothing if we aren't in
2141eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // background mode currently). This is primarily needed for unit tests,
2154a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // because in an actual running system we'd get an APP_TERMINATING
2164a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // notification before being destroyed.
2174a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  EndBackgroundMode();
2184a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek}
219c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu
220c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu// static
2214a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenekvoid BackgroundModeManager::RegisterPrefs(PrefRegistrySimple* registry) {
2224a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek#if defined(OS_MACOSX)
2234a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  registry->RegisterBooleanPref(prefs::kUserRemovedLoginItem, false);
2244a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  registry->RegisterBooleanPref(prefs::kChromeCreatedLoginItem, false);
2254a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  registry->RegisterBooleanPref(prefs::kMigratedLoginItemPref, false);
2264a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek#endif
2274a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  registry->RegisterBooleanPref(prefs::kBackgroundModeEnabled, true);
228c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu}
229c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu
2301eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
231c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xuvoid BackgroundModeManager::RegisterProfile(Profile* profile) {
2321eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // We don't want to register multiple times for one profile.
2332e287540c90255e14208e7e5f43f07cb752a1fd7Ted Kremenek  DCHECK(background_mode_data_.find(profile) == background_mode_data_.end());
2342e287540c90255e14208e7e5f43f07cb752a1fd7Ted Kremenek  BackgroundModeInfo bmd(new BackgroundModeData(current_command_id_++,
2352e287540c90255e14208e7e5f43f07cb752a1fd7Ted Kremenek                                                profile));
2369c378f705405d37f49795d5e915989de774fe11fTed Kremenek  background_mode_data_[profile] = bmd;
237c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu
2381eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // Initially set the name for this background mode data.
239c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  size_t index = profile_cache_->GetIndexOfProfileWithPath(profile->GetPath());
240d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  string16 name = l10n_util::GetStringUTF16(IDS_PROFILES_DEFAULT_NAME);
241d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  if (index != std::string::npos)
242d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek    name = profile_cache_->GetNameOfProfileAtIndex(index);
243d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  bmd->SetName(name);
244c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu
245c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  // Listen for when extensions are loaded or add the background permission so
246c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  // we can display a "background app installed" notification and enter
247c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  // "launch on login" mode on the Mac.
2484a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
24938b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu                 content::Source<Profile>(profile));
2504241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
251d2592a34a059e7cbb2b11dc53649ac4912422909Argyrios Kyrtzidis                 content::Source<Profile>(profile));
252e96de2dfde487211fb52f9139cdcae64d051a406Zhongxing Xu
2534241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek
254626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  // Check for the presence of background apps after all extensions have been
2551eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // loaded, to handle the case where an extension has been manually removed
256626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  // while Chrome was not running.
2570b506a11425daafe85ada555dd37da2d9fea76f0Ted Kremenek  registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
2580b506a11425daafe85ada555dd37da2d9fea76f0Ted Kremenek                 content::Source<Profile>(profile));
2590b506a11425daafe85ada555dd37da2d9fea76f0Ted Kremenek
260626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  bmd->applications_->AddObserver(this);
2614241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek
262626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  // If we're adding a new profile and running in multi-profile mode, this new
263626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  // profile should be added to the status icon if one currently exists.
264626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  if (in_background_mode_ && status_icon_)
26538b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu    UpdateStatusTrayIconContextMenu();
26638b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu}
26738b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu
26838b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu// static
2695fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenekvoid BackgroundModeManager::LaunchBackgroundApplication(
2705fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek    Profile* profile,
2715fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek    const Extension* extension) {
2721eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  OpenApplication(AppLaunchParams(profile, extension, NEW_FOREGROUND_TAB));
27339b4c6c98d391b25c376782cf92346aa88c96f7eTed Kremenek}
27439b4c6c98d391b25c376782cf92346aa88c96f7eTed Kremenek
275d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenekbool BackgroundModeManager::IsBackgroundModeActive() {
276d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  return in_background_mode_;
2772ac58b7c09938bb28c51c7cd2deada609b75f94cTed Kremenek}
278d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek
279d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenekint BackgroundModeManager::NumberOfBackgroundModeData() {
280626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek  return background_mode_data_.size();
281d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek}
2824d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose
2834d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose///////////////////////////////////////////////////////////////////////////////
2844d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose//  BackgroundModeManager, content::NotificationObserver overrides
2854d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rosevoid BackgroundModeManager::Observe(
2862ac58b7c09938bb28c51c7cd2deada609b75f94cTed Kremenek    int type,
2872ac58b7c09938bb28c51c7cd2deada609b75f94cTed Kremenek    const content::NotificationSource& source,
2884d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose    const content::NotificationDetails& details) {
2894241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek  switch (type) {
29038b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu    case chrome::NOTIFICATION_EXTENSIONS_READY:
2916800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks      // Extensions are loaded, so we don't need to manually keep the browser
2926800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks      // process alive any more when running in no-startup-window mode.
29338b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      EndKeepAliveForStartup();
2946800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks      break;
29538b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu
2968bef8238181a30e52dea380789a7e2d760eac532Ted Kremenek    case chrome::NOTIFICATION_EXTENSION_LOADED: {
2976800ba622e4edf287801ac69c42c61e7e294b06bAnna Zaks        Extension* extension = content::Details<Extension>(details).ptr();
2986bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines        Profile* profile = content::Source<Profile>(source).ptr();
2991eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump        if (BackgroundApplicationListModel::IsBackgroundApp(
30038b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu                *extension, profile)) {
301c77a55126fcad66fb086f8e100a494caa2496a2dZhongxing Xu          // Extensions loaded after the ExtensionsService is ready should be
30238b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu          // treated as new installs.
3034a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek          if (extensions::ExtensionSystem::Get(profile)->extension_service()->
3044241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek                  is_ready()) {
3059c378f705405d37f49795d5e915989de774fe11fTed Kremenek            bool is_being_reloaded = false;
306ede5a4ba111f0590879670b6cb07f4d6d0bd9075Ted Kremenek            CheckReloadStatus(extension, &is_being_reloaded);
307ede5a4ba111f0590879670b6cb07f4d6d0bd9075Ted Kremenek            // No need to show the notification if we showed to the user
308ede5a4ba111f0590879670b6cb07f4d6d0bd9075Ted Kremenek            // previously for this app.
3094241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek            if (!is_being_reloaded)
3104241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek              OnBackgroundAppInstalled(extension);
3119c378f705405d37f49795d5e915989de774fe11fTed Kremenek          }
312ede5a4ba111f0590879670b6cb07f4d6d0bd9075Ted Kremenek        }
313ede5a4ba111f0590879670b6cb07f4d6d0bd9075Ted Kremenek      }
314ede5a4ba111f0590879670b6cb07f4d6d0bd9075Ted Kremenek      break;
31538b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu    case chrome::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED: {
3169d0064e802e81d0833e8ccab8978b17c0bac3625Ted Kremenek        UpdatedExtensionPermissionsInfo* info =
3174a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek            content::Details<UpdatedExtensionPermissionsInfo>(details).ptr();
318d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek        if (info->permissions->HasAPIPermission(
319d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek                extensions::APIPermission::kBackground) &&
3204241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek            info->reason == UpdatedExtensionPermissionsInfo::ADDED) {
3214a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek          // Turned on background permission, so treat this as a new install.
3221eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump          OnBackgroundAppInstalled(info->extension);
32339b4c6c98d391b25c376782cf92346aa88c96f7eTed Kremenek        }
32439b4c6c98d391b25c376782cf92346aa88c96f7eTed Kremenek      }
325c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu      break;
3264241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek    case chrome::NOTIFICATION_APP_TERMINATING:
32738b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      // Make sure we aren't still keeping the app alive (only happens if we
32838b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      // don't receive an EXTENSIONS_READY notification for some reason).
329626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek      EndKeepAliveForStartup();
330626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek      // Performing an explicit shutdown, so exit background mode (does nothing
331626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek      // if we aren't in background mode currently).
332626719bd2c09e27fe7c182724a812d27f59e3819Ted Kremenek      EndBackgroundMode();
33338b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      // Shutting down, so don't listen for any more notifications so we don't
33438b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      // try to re-enter/exit background mode again.
3351eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      registrar_.RemoveAll();
33638b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      for (BackgroundModeInfoMap::iterator it =
3377ebde953bb050caa69f791fc1de449d435c6a36fTed Kremenek               background_mode_data_.begin();
33838b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu           it != background_mode_data_.end();
3391eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump           ++it) {
34038b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu        it->second->applications_->RemoveObserver(this);
3411eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      }
34238b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      break;
3431eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump    default:
34438b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu      NOTREACHED();
3451eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      break;
34638b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu  }
3471eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump}
34838b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu
3491eb4433ac451dc16f4133a88af2d002ac26c58efMike Stumpvoid BackgroundModeManager::OnBackgroundModeEnabledPrefChanged() {
3501eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  if (IsBackgroundModePrefEnabled())
3514241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek    EnableBackgroundMode();
35238b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu  else
3531eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump    DisableBackgroundMode();
35438b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu}
3551eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
35638b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu///////////////////////////////////////////////////////////////////////////////
3571eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump//  BackgroundModeManager, BackgroundApplicationListModel::Observer overrides
35838b02b912e1a55c912f603c4369431264d36a381Zhongxing Xuvoid BackgroundModeManager::OnApplicationDataChanged(
35938b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu    const Extension* extension, Profile* profile) {
3605fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek  UpdateStatusTrayIconContextMenu();
3615fe4d9deb543a19f557e3d85c5f33867af97cd96Ted Kremenek}
36238b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu
36338b02b912e1a55c912f603c4369431264d36a381Zhongxing Xuvoid BackgroundModeManager::OnApplicationListChanged(Profile* profile) {
36438b02b912e1a55c912f603c4369431264d36a381Zhongxing Xu  if (!IsBackgroundModePrefEnabled())
365c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose    return;
366c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose
367c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  // Update the profile cache with the fact whether background apps are running
368c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  // for this profile.
369c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  size_t profile_index = profile_cache_->GetIndexOfProfileWithPath(
370c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose      profile->GetPath());
371c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  if (profile_index != std::string::npos) {
372c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose    profile_cache_->SetBackgroundStatusOfProfileAtIndex(
373c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose        profile_index, GetBackgroundAppCountForProfile(profile) != 0);
374c9963132736782d0c9178c744b3e2307cfb98a08Jordan Rose  }
3750f3a34fb7fea37ebfbcba8b400ccb697b9559b49Jordan Rose
3766bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines  if (!ShouldBeInBackgroundMode()) {
3776bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89Stephen Hines    // We've uninstalled our last background app, make sure we exit background
378d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek    // mode and no longer launch on startup.
379d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek    EnableLaunchOnStartup(false);
3802ac58b7c09938bb28c51c7cd2deada609b75f94cTed Kremenek    EndBackgroundMode();
3814d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose  } else {
3824d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose    // We have at least one background app running - make sure we're in
3834d9e497a2b1eab3b1214848216050c64fc3acfd6Jordan Rose    // background mode.
384d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek    if (!in_background_mode_) {
385d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek      // We're entering background mode - make sure we have launch-on-startup
386d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek      // enabled. On Mac, the platform-specific code tracks whether the user
3872ac58b7c09938bb28c51c7cd2deada609b75f94cTed Kremenek      // has deleted a login item in the past, and if so, no login item will
388841c96a885789afea9d32d1d842033768c6d2b19Ted Kremenek      // be created (to avoid overriding the specific user action).
3894e9c0854382d37325771b50f6cf899a75119fa24Ted Kremenek      EnableLaunchOnStartup(true);
3904e9c0854382d37325771b50f6cf899a75119fa24Ted Kremenek
3914e9c0854382d37325771b50f6cf899a75119fa24Ted Kremenek      StartBackgroundMode();
3924e9c0854382d37325771b50f6cf899a75119fa24Ted Kremenek    }
393841c96a885789afea9d32d1d842033768c6d2b19Ted Kremenek    // List of applications changed so update the UI.
394841c96a885789afea9d32d1d842033768c6d2b19Ted Kremenek    UpdateStatusTrayIconContextMenu();
395841c96a885789afea9d32d1d842033768c6d2b19Ted Kremenek  }
3964241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek}
397cf118d41f7930a18dce97416ef7834a62642f587Ted Kremenek
398f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek///////////////////////////////////////////////////////////////////////////////
399c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu//  BackgroundModeManager, ProfileInfoCacheObserver overrides
400f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenekvoid BackgroundModeManager::OnProfileAdded(const base::FilePath& profile_path) {
4011eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  ProfileInfoCache& cache =
402f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek      g_browser_process->profile_manager()->GetProfileInfoCache();
4039c378f705405d37f49795d5e915989de774fe11fTed Kremenek  string16 profile_name = cache.GetNameOfProfileAtIndex(
404c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu      cache.GetIndexOfProfileWithPath(profile_path));
405f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek  // At this point, the profile should be registered with the background mode
406f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek  // manager, but when it's actually added to the cache is when its name is
4071eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // set so we need up to update that with the background_mode_data.
408f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek  for (BackgroundModeInfoMap::const_iterator it =
4091eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump       background_mode_data_.begin();
4109c378f705405d37f49795d5e915989de774fe11fTed Kremenek       it != background_mode_data_.end();
411c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu       ++it) {
412f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek    if (it->first->GetPath() == profile_path) {
4131eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      it->second->SetName(profile_name);
414c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu      UpdateStatusTrayIconContextMenu();
415c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu      return;
416f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek    }
417fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek  }
418fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek}
4191aae01a8308d2f8e31adab3f4d7ac35543aac680Anna Zaks
420fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenekvoid BackgroundModeManager::OnProfileWillBeRemoved(
421fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek    const base::FilePath& profile_path) {
422fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek  ProfileInfoCache& cache =
4232d950b15b2b2b650b102ecf0c6b50b45e0cb6a8aAnna Zaks      g_browser_process->profile_manager()->GetProfileInfoCache();
424fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek  string16 profile_name = cache.GetNameOfProfileAtIndex(
425fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek      cache.GetIndexOfProfileWithPath(profile_path));
426fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek  // Remove the profile from our map of profiles.
427fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek  BackgroundModeInfoMap::iterator it =
428fee96e043108b6e24e7d4c5464bf89ac970a7f81Ted Kremenek      GetBackgroundModeIterator(profile_name);
4291eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  // If a profile isn't running a background app, it may not be in the map.
430f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek  if (it != background_mode_data_.end()) {
431f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek    background_mode_data_.erase(it);
4321eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump    UpdateStatusTrayIconContextMenu();
433f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek  }
434f6f5ef4aaa66b60270e84d1fe1292886369d2f38Ted Kremenek}
4351eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4361eb4433ac451dc16f4133a88af2d002ac26c58efMike Stumpvoid BackgroundModeManager::OnProfileNameChanged(
4375a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis    const base::FilePath& profile_path,
4385a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis    const string16& old_profile_name) {
4394241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek  ProfileInfoCache& cache =
4404241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek      g_browser_process->profile_manager()->GetProfileInfoCache();
4414a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  string16 new_profile_name = cache.GetNameOfProfileAtIndex(
4424a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      cache.GetIndexOfProfileWithPath(profile_path));
4434a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  BackgroundModeInfoMap::const_iterator it =
4449ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenek      GetBackgroundModeIterator(old_profile_name);
4459ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenek  // We check that the returned iterator is valid due to unittests, but really
446c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu  // this should only be called on profiles already known by the background
4474a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // mode manager.
4481eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  if (it != background_mode_data_.end()) {
4494a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    it->second->SetName(new_profile_name);
4504a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    UpdateStatusTrayIconContextMenu();
4514a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  }
4521eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump}
4534a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek
4544a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek///////////////////////////////////////////////////////////////////////////////
4554a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek//  BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides
4561eb4433ac451dc16f4133a88af2d002ac26c58efMike Stumpvoid BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) {
4574a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // When a browser window is necessary, we use the first profile. The windows
4584a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // opened for these commands are not profile-specific, so any profile would
4594a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  // work and the first is convenient.
4601eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  BackgroundModeData* bmd = background_mode_data_.begin()->second.get();
4614a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  switch (command_id) {
4624a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    case IDC_ABOUT:
4634a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      chrome::ShowAboutChrome(bmd->GetBrowserWindow());
4641eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      break;
4654a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    case IDC_TASK_MANAGER:
4664a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      chrome::OpenTaskManager(bmd->GetBrowserWindow());
4674a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      break;
4684a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    case IDC_EXIT:
4691eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      content::RecordAction(UserMetricsAction("Exit"));
4709ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenek      chrome::CloseAllBrowsers();
4719ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenek      break;
472c5619d901a68dc27a9e310a6a831f03efebcd950Zhongxing Xu    case IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND: {
4734a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      // Background mode must already be enabled (as otherwise this menu would
4741eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      // not be visible).
4754a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      DCHECK(IsBackgroundModePrefEnabled());
4764a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      DCHECK(chrome::WillKeepAlive());
4774a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek
4781eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      // Set the background mode pref to "disabled" - the resulting notification
4794a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      // will result in a call to DisableBackgroundMode().
4804a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      PrefService* service = g_browser_process->local_state();
4814a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      DCHECK(service);
4821eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      service->SetBoolean(prefs::kBackgroundModeEnabled, false);
4834a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      break;
4844a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    }
4854a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    default:
4861eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump      bmd->ExecuteCommand(command_id, event_flags);
4874a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek      break;
4884a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek  }
4894a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek}
4901eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
4914a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek
4924a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek///////////////////////////////////////////////////////////////////////////////
4934a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek//  BackgroundModeManager, private
4944a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenekvoid BackgroundModeManager::EndKeepAliveForStartup() {
4951eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  if (keep_alive_for_startup_) {
4964a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    keep_alive_for_startup_ = false;
4974a0f5f1646637fcf90eb236b5a46f40e5a5dd739Ted Kremenek    // We call this via the message queue to make sure we don't try to end
4984241b3d1ad87e9a593bbc6cdf0f49435d5aec235Ted Kremenek    // keep-alive (which can shutdown Chrome) before the message loop has
499    // started.
500    base::MessageLoop::current()->PostTask(FROM_HERE,
501                                           base::Bind(&chrome::EndKeepAlive));
502  }
503}
504
505void BackgroundModeManager::StartBackgroundMode() {
506  DCHECK(ShouldBeInBackgroundMode());
507  // Don't bother putting ourselves in background mode if we're already there
508  // or if background mode is disabled.
509  if (in_background_mode_)
510    return;
511
512  // Mark ourselves as running in background mode.
513  in_background_mode_ = true;
514
515  UpdateKeepAliveAndTrayIcon();
516
517  content::NotificationService::current()->Notify(
518      chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
519      content::Source<BackgroundModeManager>(this),
520      content::Details<bool>(&in_background_mode_));
521}
522
523void BackgroundModeManager::EndBackgroundMode() {
524  if (!in_background_mode_)
525    return;
526  in_background_mode_ = false;
527
528  UpdateKeepAliveAndTrayIcon();
529
530  content::NotificationService::current()->Notify(
531      chrome::NOTIFICATION_BACKGROUND_MODE_CHANGED,
532      content::Source<BackgroundModeManager>(this),
533      content::Details<bool>(&in_background_mode_));
534}
535
536void BackgroundModeManager::EnableBackgroundMode() {
537  DCHECK(IsBackgroundModePrefEnabled());
538  // If background mode should be enabled, but isn't, turn it on.
539  if (!in_background_mode_ && ShouldBeInBackgroundMode()) {
540    StartBackgroundMode();
541    EnableLaunchOnStartup(true);
542  }
543}
544
545void BackgroundModeManager::DisableBackgroundMode() {
546  DCHECK(!IsBackgroundModePrefEnabled());
547  // If background mode is currently enabled, turn it off.
548  if (in_background_mode_) {
549    EndBackgroundMode();
550    EnableLaunchOnStartup(false);
551  }
552}
553
554void BackgroundModeManager::SuspendBackgroundMode() {
555  background_mode_suspended_ = true;
556  UpdateKeepAliveAndTrayIcon();
557}
558
559void BackgroundModeManager::ResumeBackgroundMode() {
560  background_mode_suspended_ = false;
561  UpdateKeepAliveAndTrayIcon();
562}
563
564void BackgroundModeManager::UpdateKeepAliveAndTrayIcon() {
565  if (in_background_mode_ && !background_mode_suspended_) {
566    if (!keeping_alive_) {
567      keeping_alive_ = true;
568      chrome::StartKeepAlive();
569    }
570    CreateStatusTrayIcon();
571    return;
572  }
573
574  RemoveStatusTrayIcon();
575  if (keeping_alive_) {
576    keeping_alive_ = false;
577    chrome::EndKeepAlive();
578  }
579}
580
581void BackgroundModeManager::OnBrowserAdded(Browser* browser) {
582  ResumeBackgroundMode();
583}
584
585int BackgroundModeManager::GetBackgroundAppCount() const {
586  int count = 0;
587  // Walk the BackgroundModeData for all profiles and count the number of apps.
588  for (BackgroundModeInfoMap::const_iterator it =
589       background_mode_data_.begin();
590       it != background_mode_data_.end();
591       ++it) {
592    count += it->second->GetBackgroundAppCount();
593  }
594  DCHECK(count >= 0);
595  return count;
596}
597
598int BackgroundModeManager::GetBackgroundAppCountForProfile(
599    Profile* const profile) const {
600  BackgroundModeData* bmd = GetBackgroundModeData(profile);
601  return bmd->GetBackgroundAppCount();
602}
603
604bool BackgroundModeManager::ShouldBeInBackgroundMode() const {
605  return IsBackgroundModePrefEnabled() &&
606      (GetBackgroundAppCount() > 0 || keep_alive_for_test_);
607}
608
609void BackgroundModeManager::OnBackgroundAppInstalled(
610    const Extension* extension) {
611  // Background mode is disabled - don't do anything.
612  if (!IsBackgroundModePrefEnabled())
613    return;
614
615  // Ensure we have a tray icon (needed so we can display the app-installed
616  // notification below).
617  EnableBackgroundMode();
618  ResumeBackgroundMode();
619
620  // Notify the user that a background app has been installed.
621  if (extension) {  // NULL when called by unit tests.
622    DisplayAppInstalledNotification(extension);
623  }
624}
625
626void BackgroundModeManager::CheckReloadStatus(
627    const Extension* extension,
628    bool* is_being_reloaded) {
629    // Walk the BackgroundModeData for all profiles to see if one of their
630    // extensions is being reloaded.
631    for (BackgroundModeInfoMap::const_iterator it =
632             background_mode_data_.begin();
633         it != background_mode_data_.end();
634         ++it) {
635      Profile* profile = it->first;
636      // If the extension is being reloaded, no need to show a notification.
637      if (profile->GetExtensionService()->IsBeingReloaded(extension->id()))
638        *is_being_reloaded = true;
639    }
640}
641
642void BackgroundModeManager::CreateStatusTrayIcon() {
643  // Only need status icons on windows/linux. ChromeOS doesn't allow exiting
644  // Chrome and Mac can use the dock icon instead.
645
646  // Since there are multiple profiles which share the status tray, we now
647  // use the browser process to keep track of it.
648#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
649  if (!status_tray_)
650    status_tray_ = g_browser_process->status_tray();
651#endif
652
653  // If the platform doesn't support status icons, or we've already created
654  // our status icon, just return.
655  if (!status_tray_ || status_icon_)
656    return;
657
658  // TODO(rlp): Status tray icon should have submenus for each profile.
659  gfx::ImageSkia* image_skia = ui::ResourceBundle::GetSharedInstance().
660      GetImageSkiaNamed(IDR_STATUS_TRAY_ICON);
661
662  status_icon_ = status_tray_->CreateStatusIcon(
663      StatusTray::BACKGROUND_MODE_ICON,
664      *image_skia,
665      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
666  if (!status_icon_)
667    return;
668  UpdateStatusTrayIconContextMenu();
669}
670
671void BackgroundModeManager::UpdateStatusTrayIconContextMenu() {
672  // Ensure we have a tray icon if appropriate.
673  UpdateKeepAliveAndTrayIcon();
674
675  // If we don't have a status icon or one could not be created succesfully,
676  // then no need to continue the update.
677  if (!status_icon_)
678    return;
679
680  // We should only get here if we have a profile loaded, or if we're running
681  // in test mode.
682  if (background_mode_data_.empty()) {
683    DCHECK(keep_alive_for_test_);
684    return;
685  }
686
687  // TODO(rlp): Add current profile color or indicator.
688  // Create a context menu item for Chrome.
689  scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this));
690  // Add About item
691  menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT));
692  menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER);
693  menu->AddSeparator(ui::NORMAL_SEPARATOR);
694
695  if (profile_cache_->GetNumberOfProfiles() > 1) {
696    std::vector<BackgroundModeData*> bmd_vector;
697    for (BackgroundModeInfoMap::iterator it =
698         background_mode_data_.begin();
699         it != background_mode_data_.end();
700         ++it) {
701       bmd_vector.push_back(it->second.get());
702    }
703    std::sort(bmd_vector.begin(), bmd_vector.end(),
704              &BackgroundModeData::BackgroundModeDataCompare);
705    int profiles_with_apps = 0;
706    for (std::vector<BackgroundModeData*>::const_iterator bmd_it =
707         bmd_vector.begin();
708         bmd_it != bmd_vector.end();
709         ++bmd_it) {
710      BackgroundModeData* bmd = *bmd_it;
711      // We should only display the profile in the status icon if it has at
712      // least one background app.
713      if (bmd->GetBackgroundAppCount() > 0) {
714        StatusIconMenuModel* submenu = new StatusIconMenuModel(bmd);
715        bmd->BuildProfileMenu(submenu, menu.get());
716        profiles_with_apps++;
717      }
718    }
719    // We should only be displaying the status tray icon if there is at least
720    // one profile with a background app.
721    DCHECK_GT(profiles_with_apps, 0);
722  } else {
723    // We should only have one profile in the cache if we are not
724    // using multi-profiles. If keep_alive_for_test_ is set, then we may not
725    // have any profiles in the cache.
726    DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) ||
727           keep_alive_for_test_);
728    background_mode_data_.begin()->second->BuildProfileMenu(menu.get(), NULL);
729  }
730
731  menu->AddSeparator(ui::NORMAL_SEPARATOR);
732  menu->AddCheckItemWithStringId(
733      IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
734      IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND);
735  menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
736                            true);
737
738  PrefService* service = g_browser_process->local_state();
739  DCHECK(service);
740  bool enabled =
741      service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled);
742  menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND,
743                            enabled);
744
745  menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT);
746
747  context_menu_ = menu.get();
748  status_icon_->SetContextMenu(menu.Pass());
749}
750
751void BackgroundModeManager::RemoveStatusTrayIcon() {
752  if (status_icon_)
753    status_tray_->RemoveStatusIcon(status_icon_);
754  status_icon_ = NULL;
755  context_menu_ = NULL;
756}
757
758BackgroundModeManager::BackgroundModeData*
759BackgroundModeManager::GetBackgroundModeData(Profile* const profile) const {
760  DCHECK(background_mode_data_.find(profile) != background_mode_data_.end());
761  return background_mode_data_.find(profile)->second.get();
762}
763
764BackgroundModeManager::BackgroundModeInfoMap::iterator
765BackgroundModeManager::GetBackgroundModeIterator(
766    const string16& profile_name) {
767  BackgroundModeInfoMap::iterator profile_it =
768      background_mode_data_.end();
769  for (BackgroundModeInfoMap::iterator it =
770       background_mode_data_.begin();
771       it != background_mode_data_.end();
772       ++it) {
773    if (it->second->name() == profile_name) {
774      profile_it = it;
775    }
776  }
777  return profile_it;
778}
779
780bool BackgroundModeManager::IsBackgroundModePrefEnabled() const {
781  PrefService* service = g_browser_process->local_state();
782  DCHECK(service);
783  return service->GetBoolean(prefs::kBackgroundModeEnabled);
784}
785