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